I am having some strange bug, which I cannot grasp where it comes from. I am writing it in js in Google Script environment.
function tester() {
var pdf = [[0,5],[1,5],[2,40],[3,50]]; // some pdf as a 2d array
var tuple = [0,0,0,0]; //the resulting pdf from the test
var rand = 0;
for (var i = 0; i<100; i++){ //100 times initialize a random variable and then catch the result into the tuple
rand = getRandomN(pdf);
if (rand==0){tuple[0]+=1} //if the outcome==0 then add 1 to the first element of the tuple
else if (rand==1){tuple[1]+=1}
else if (rand==2){tuple[2]+=1}
else if (rand==3){tuple[3]+=1}
}
Logger.log(tuple);
}
getRandomN(pdf) returns one outcome according to the pdf
The problem is that the tuple always returns all zeros with 1 at some of the places. It looks like the randomizer works just fine, but looping is gone through only once.
Does anyone have a hint?
UPDATE:
function getRandomN(pdf) {
var result = 0;
var rand = getRandomInt(0,10000)/100;
for (var i=1; i<pdf.length; i++){
pdf[i][1] = pdf[i][1] + pdf[i-1][1];
}
if (pdf[pdf.length-1][1] != 100){return undefined}
//Logger.log(rand);
for (var i=0; i<pdf.length; i++){
if (rand<=pdf[i][1]){result=pdf[i][0]; break}
}
Logger.log(pdf);
return result;
}
And the standard function from the Mozilla
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
The reason for this is:
if (pdf[pdf.length-1][1] != 100){return undefined;}
here you are returning undefined if you return 0 or any of rand first index then it will display proper tuple and you can see the loop count.
try running this:
function tester() {
var pdf = [[0,5],[1,5],[2,40],[3,50]]; // some pdf as a 2d array
var tuple = [0,0,0,0]; //the resulting pdf from the test
var rand = 0;
for (var i = 0; i<100; i++){ //100 times initialize a random variable and then catch the result into the tuple
rand = getRandomN(pdf);
tuple[rand] += 1;
}
console.log(tuple);
document.write(tuple);
}
function getRandomN(pdf) {
var result = 0;
var rand = getRandomInt(0,10000)/100;
// console.log(rand);
for (var i=1; i<pdf.length; i++){
pdf[i][1] = pdf[i][1] + pdf[i-1][1];
}
if (pdf[pdf.length-1][1] != 100){return 0;}//return any of 0,1,2,3 to test your code.
for (var i=0; i<pdf.length; i++){
if (rand<=pdf[i][1]){result=pdf[i][0]; break}
}
// console.log(pdf);
return result;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
tester();
I think I know why. Because the scope of pdf is exactly global within the tester() and I am changing it within the getRandomN(pdf), hence, it is increasing all the time, and after the first run it gets updated and I am calculating already from a new pdf, where the last element of the pdf (i.e. cdf) will never be equal to 100.
UPDATE:
Just if you are interested in the correct code that is working. The part of mapping pdf to cdf is not the most beautiful one. I'd appreciate improvement hints, but it works just fine. Kudos to the contributors for pointing into the right direction.
function getRandomN(pdf) {
var result = 0;
var rand = getRandomInt(0,10000)/100;
var cdf = [];
//construct the cdf
for (var i=1; i<pdf.length; i++){
//handle the first unchanged element
cdf[0]=[];
cdf[0][1] = pdf[0][1];
cdf[0][0] = pdf[0][0];
cdf[i]=[];
cdf[i][1] = pdf[i][1] + cdf[i-1][1];
cdf[i][0] = pdf[i][0];//add all outcomes to the array's first column
}
if (cdf[cdf.length-1][1] != 100){return undefined}
//Logger.log(rand);
for (var i=0; i<cdf.length; i++){
if (rand<=cdf[i][1]){result=cdf[i][0]; break}
}
//Logger.log(cdf);
return result;
}
Related
I need help with writing some code that will create a random number from an array of 12 numbers and print it 9 times without dupes. This has been tough for me to accomplish. Any ideas?
var nums = [1,2,3,4,5,6,7,8,9,10,11,12];
var gen_nums = [];
function in_array(array, el) {
for(var i = 0 ; i < array.length; i++)
if(array[i] == el) return true;
return false;
}
function get_rand(array) {
var rand = array[Math.floor(Math.random()*array.length)];
if(!in_array(gen_nums, rand)) {
gen_nums.push(rand);
return rand;
}
return get_rand(array);
}
for(var i = 0; i < 9; i++) {
console.log(get_rand(nums));
}
The most effective and efficient way to do this is to shuffle your numbers then print the first nine of them. Use a good shuffle algorithm.What Thilo suggested will give you poor results. See here.
Edit
Here's a brief Knuth Shuffle algorithm example:
void shuffle(vector<int> nums)
{
for (int i = nums.size()-1; i >= 0; i--)
{
// this line is really shorthand, but gets the point across, I hope.
swap(nums[i],nums[rand()%i]);
}
}
Try this once:
//Here o is the array;
var testArr = [6, 7, 12, 15, 17, 20, 21];
shuffle = function(o){ //v1.0
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
shuffle(testArr);
This is relatively simple to do, the theory behind it is creating another array which keeps track of which elements of the array you have used.
var tempArray = new Array(12),i,r;
for (i=0;i<9;i++)
{
r = Math.floor(Math.random()*12); // Get a random index
if (tempArray[r] === undefined) // If the index hasn't been used yet
{
document.write(numberArray[r]); // Display it
tempArray[r] = true; // Flag it as have been used
}
else // Otherwise
{
i--; // Try again
}
}
Other methods include shuffling the array, removing used elements from the array, or moving used elements to the end of the array.
If I understand you correctly, you want to shuffle your array.
Loop a couple of times (length of array should do), and in every iteration, get two random array indexes and swap the two elements there. (Update: if you are really serious about this, this may not be the best algorithm).
You can then print the first nine array elements, which will be in random order and not repeat.
Here is a generic way of getting random numbers between min and max without duplicates:
function inArray(arr, el) {
for(var i = 0 ; i < arr.length; i++)
if(arr[i] == el) return true;
return false;
}
function getRandomIntNoDuplicates(min, max, DuplicateArr) {
var RandomInt = Math.floor(Math.random() * (max - min + 1)) + min;
if (DuplicateArr.length > (max-min) ) return false; // break endless recursion
if(!inArray(DuplicateArr, RandomInt)) {
DuplicateArr.push(RandomInt);
return RandomInt;
}
return getRandomIntNoDuplicates(min, max, DuplicateArr); //recurse
}
call with:
var duplicates =[];
for (var i = 1; i <= 6 ; i++) {
console.log(getRandomIntNoDuplicates(1,10,duplicates));
}
const nums = [1,2,3,4,5,6,7,8,9,10,11,12];
for(var i = 1 ; i < 10; i++){
result = nums[Math.floor(Math.random()*nums.length)];
const index = nums.indexOf(result);
nums.splice(index, 1);
console.log(i+' - '+result);
}
Edit: Actually the logic is wrong here.
I solved it using Python3 with a dictionary that updates the last index at which a letter is seen. In dynamic programming lingo, it is similar to L.I.S (longest increasing subsequence).
If anyone knows how to solve this without using a dictionary, please comment because I learned DP in school and those lessons only used arrays so it should be possible with just arrays.
Original question:
I am trying Leetcode, 3. Longest Substring Without Repeating Characters.
I can solve this in Python making a 2D table for dynamic programming.
But in JavaScript which I am sort of new to, I am getting an error.
evalmachine.<anonymous>:41
var top = T[i-1][j]
^
TypeError: Cannot read property '1' of undefined
at lengthOfLongestSubstring (evalmachine.<anonymous>:4
My code:
/**
* #param {string} s
* #return {number}
*/
var lengthOfLongestSubstring = function(s) {
//empty string
if (s.length <= 0){
return 0
}
//initialize dict
var dict = {};
//initialize 2D table T
var T = new Array(s.length)
for (var i = 0; i<s.length; i++){
T[i] = new Array(s.length);
}
//base cases are diagonals
for (var i = 0; i < T.length; i++){
for (var j=0; j<T.length; j++){
if(i==j){
T[i][j] = 1;
}
else{
T[i][j] = 0;
}
}
}
//put base case in dict
//dict[s[0]]=1
for (var i=0; i < s.length; i++){
for (var j=i+1; j<s.length; j++){
var row_char = s.charAt(i);
var col_char = s.charAt(j);
if (row_char==col_char){
T[i][j] = 1;
}
else{
//console.log("j",j,T)
var left = T[i][j-1]
console.log(left)
var top = T[i-1][j]
console.log(top)
var bigger = Math.max(left,top);
T[i][j] = bigger + 1
}
}
}
//iterate each row to get max
var high = Number.MIN_SAFE_INTEGER;
for (var i = 0; i < s.length; i++){
if(T[i][s.length-1] > high){
high = T[i][s.length-1];
}
}
return high;
};
It is letting me fill the table with 0's and base case of 1 indexing like T[i][j] but then complaining about indexing like that to get the value which I don't understand.
I looked at this: How to get value at a specific index of array In JavaScript?
But it does not really say anything different.
On the first iteration of the loop following the //put base case in dict comment i is 0.
You're then attempting to access T[i-1][j], which is the equivalent of T[-1][j].
Because T doesn't have a -1 index, T[-1] resolves to undefined, upon which you attempt to access index [j] and you get the error you're seeing.
I am trying to make a function that generates an array of numbers within a certain range.
A very basic question but I couldn't find an explanation...
I tried this
var newArray = [];
function makeArrey(start, last) {
var length = last - start;
for(var i = 0; i <= length; i++) {
newArray[i] = start + i;
}
return newArray;
}
makeArrey(1, 100);
alert(newArray[4]); //4 is a random number to see if it works, it dont work
Your code is working perfectly fine. You're just forgetting that arrays start at index 0. So when you do something like newArray[4] it returns the 5th element of the array, which is 5 in your case.
For the sake of fixing your code so that it behaves nicer, do this:
function makeArray(start, last) {
var range = [];
var length = last - start;
for(var i = 0; i <= length; i++) {
range[i] = start + i;
}
return range;
}
var newArray = makeArray(1, 100);
I want to push arrays containing random numbers (0 to 10) into a bigger array once the total of its contents is about to exceed 30. But the output is messed up.
var bigarray = new Array();
var smallarray = new Array();
var randNum = 0;
var total = 0;
for (var i = 0; i<10; i++){
randNum = (10*Math.random()).toFixed(0);
total = total + randNum;
if(total>30) {
bigarray.push(smallarray)
smallarray.length=0;
smallarray.push(randNum);
total = randNum;
} else {
smallarray.push(randNum);
}
}
alert(" BIG ARRAY IS "+bigarray);
two wrong things are visible on the first sight in the code
(1) instead of
randNum = (10*Math.random()).toFixed(0);
you probably want
randNum = Math.floor(11*Math.random());
Math.floor instead of toFixed() - see #kennebec comment
11 instead of 10 to return numbers 0 to 10, as 0 <= Math.random() < 1
(2) the following line pushes (many times) the reference to the same smallarray object.
bigarray.push(smallarray);
In the next step you clear the array with smallarray.length = 0. Because the array is not copied to the bigarray, but only referenced, the generated items are lost.
EDIT: I read your question wrong - the rest of the answer is fixed
You probably want to push the duplicate of the smallarray into bigarray, so replace the line above with the following:
bigarray.push(smallarray.slice(0));
You need another loop inside the main one to populate the smallarray, something like:
var bigarray = new Array();
for (var i = 0; i<10; i++){
// moving the variable declarations inside this loop means they are re-set for each small array
var smallarray = new Array();
// create the first entry for the small array
var randNum = Math.floor(11*Math.random());
var total = randNum;
// loop to populate the small array
while(total <= 30){
smallarray.push(randNum);
randNum = Math.floor(11*Math.random());
total += randNum;
}
bigarray.push(smallarray)
}
I made changes to you code and came up with this.
var bigarray = [];
var smallarray = [];
var randNum = 0;
var total = 0;
for (var i = 0; i < 10; i += 1) {
randNum = Math.floor(10 * Math.random()); // you will never have a value of 10?
total = total + randNum;
if (total > 30) {
bigarray.push(smallarray.slice())
smallarray.length = 0;
smallarray.push(randNum);
total = randNum;
} else {
smallarray.push(randNum);
}
}
alert(" BIG ARRAY IS " + bigarray);
On jsfiddle
Things I changed were:
Ran the code through a beautifier
Changed your use of new Array to []
{} and []
Use {} instead of new Object(). Use [] instead of new Array().
Because Object and Array can be overwritten by the user
Changed ++ to += 1
This pattern can be confusing.
Check out Code Conventions for the JavaScript Programming Language and jslint
Added array.slice when you push smallarray to bigarray, this makes a copy in this case. It is important to understand how javascript works, read Is JavaScript a pass-by-reference or pass-by-value language? Without using slice, which makes a copy as the array only contains primitives, when you set the length of the array to 0, then the data was lost.
Changed your use of number.toFixed to Math.floor so that randNum remains a number
Note: Math.random returns a floating-point, pseudo-random number in the range [0, 1] that is, from 0 (inclusive) up to but not including 1 (exclusive)
Whether your code now produces your expected out, I can not be sure from your description but this should be a good starting point.
var bigarray = new Array();
var smallarray = new Array();
var randNum = 0;
var total = 0;
for (var i = 0; i < 10; i++) {
for (var j = 0; j < smallarray.length; j++) {
total = total + smallarray[j];
}
if (total <= 30)
{
randNum = Math.floor((Math.random() * 10) + 1);
smallarray.push(randNum);
}
else {
bigarray.push(smallarray.slice(0));
smallarray.length = 0;
}
total = 0;
}
alert(" BIG ARRAY IS " + bigarray);
I'm creating a program that will ask a question and give 5 choices for answers.
One is pre-defined and is correct, the others I want to be random selections from a bank of answers and the entire array is to be shuffled too.
I've written something, but it has some inconsistencies.
For one, sometimes the pre-defined choice appears twice in the list (it appears to skip over my if check).
Another is that sometimes, the editor crashes when I run it.
I use for in loops and I'm worried the crash is caused by a never-ending loop.
Here's my code:
private var numberOfComponents:int;
private var maxComponents:int = 5;
//numberOfComponents returns the length property of my 'components' answer bank
componentsSelection = buildComponentSelectionList(0); //0 is the index of my correct answer
function buildComponentSelectionList(correctItemIndex){
var theArray:Array = new Array();
var indicesOfSelection:Array = getIndicesByIncluding(correctItemIndex);
Debug.Log(indicesOfSelection);
for (var i=0;i<indicesOfSelection.length;i++)
theArray.Push(components[indicesOfSelection[i]]);
return theArray;
}
function getIndicesByIncluding(correctItem){
var indicesArray:Array = new Array();
var numberOfChoices = maxComponents-1;
for(var i=0;i<numberOfChoices;i++){
var number = Mathf.Round(Random.value*(numberOfComponents-1));
addToRandomNumberSelection(indicesArray, number,correctItem);
}
indicesArray.Push(correctItem);
RandomizeArray(indicesArray);
return indicesArray;
}
function addToRandomNumberSelection(indicesArray:Array,number,correctItem){
if(indicesArray.length == 0){
indicesArray.Push(number);
} else {
var doesntExist = true;
for(var i=0;i<indicesArray.length;i++){
if(indicesArray[i] == correctItem)
doesntExist = false;
if (indicesArray[i] == number)
doesntExist = false;
}
if(doesntExist) {
indicesArray.Push(number);
} else {
addToRandomNumberSelection(indicesArray, Mathf.Round(Random.value*(numberOfComponents-1)),correctItem);
}
}
}
function RandomizeArray(arr : Array)
{
for (var i = arr.length - 1; i > 0; i--) {
var r = Random.Range(0,i);
var tmp = arr[i];
arr[i] = arr[r];
arr[r] = tmp;
}
}
The editor is Unity3D, and the code is a version of JavaScript; I think my error is a logic one, rather than a syntactical one.
I feel I've been staring at this code for too long now and I'm missing something obvious.
Can anybody help me?
You can loop through the options and determine the probability that it should be included, then shuffle the included options:
function getRandomOptions(allOptions, correctIndex, count){
var result = [allOptions[correctIndex]];
count--;
var left = allOptions.length;
for (var i = 0; count > 0; i++) {
if (i != correctIndex && Math.floor(Math.random() * left) < count) {
result.push(allOptions[i]);
count--;
}
left--;
}
shuffleArray(result);
return result;
}
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var r = Math.floor(Math.random() * i);
var tmp = arr[i];
arr[i] = arr[r];
arr[r] = tmp;
}
}
Demo: http://jsfiddle.net/Guffa/wXsjz/