views:

237

answers:

8

I need to randomize set of values in JS, and I call function randomize for example three times. How can I remember and block random generator from giving me results that it gave me previous times? I can only give one value once.

var value = Math.floor(Math.random() * 11);
A: 

Instead of using Random(), you can use time ticks which will give you a random value everytime you use them -

var random = (new Date()).getTime();


Or, you can extend the default JS Array, and add the contains method to it. Every random value you generate will be added to the array, but before adding, the contains method will check whether it already exists in the array or not.

Kirtan
I have only small range of values for example 1-10 etc...
+1  A: 

Something like this (tested):

Array.prototype.unique = function () {
    var r = new Array();
    o:for(var i = 0, n = this.length; i < n; i++)
    {
     for(var x = 0, y = r.length; x < y; x++)
     {
      if(r[x]==this[i])
      {
       continue o;
      }
     }
     r[r.length] = this[i];
    }
    return r;
}

var temp = [];

//keep going until the array size is three
while(temp.length < 3) {
    temp.push(Math.floor(Math.random() * 11));
    //unique() will remove the dupes thus affecting the length 
    temp = temp.unique();
}

alert(temp[0] + ' ' + temp[1] + ' ' + temp[2]);
karim79
What does the `o:` before the for-loop stand for?
Philipe Fatio
fphilipe, it's a label that the continue uses. If it wasn't there the continue would continue the inner loop.
Marius
@fphilipe: `o` is a label that @karim79 used for the `continue` statement.
Török Gábor
@Marius @Török Gábor, thanks guys.
karim79
A: 

Use a hash.

var values = {};
count = 0; //keep count of how many are there if you want.
while( count < something ) {
    var rand = Math.random();
    if( !values[rand] ) {
        values[rand] = true;
        count++;
    }
}

Then you can dump all of those into an array if you want - that's O(n).

var arrValues = [];
for( var p in values ) arrValues.push( new Number(p) );
geowa4
You need to have a loop here around your first section. And I'm not clear on what you mean by the comment "dump all of those into an array if you want O(n)...you've already done the work...you can't lower the O value by just putting it in a new data structure. Am I missing something?
Beska
heh, could you tel that I hadn't quite woken up yet?
geowa4
A: 

One of the wonderful randoms are provided by allocating sequential array of int:

for(i = 0; i < 100; ++i) myArr[i]=i;

And after it you shift numbers there in order Math.floor(Math.random() * myArr.length);

benefit of such approach - you get non-repeatable sequence of any length.

Dewfy
+3  A: 

Use the memoization pattern.

var randomize = (function () {
    var memo = [],
        maxlen = 10;
    return function() {
        if (memo.length === maxlen) {
            throw new Error('Out of values');
        }
        var value = Math.floor(Math.random() * (maxlen + 1));
        if (memo.indexOf(value) !== -1) {
            return randomize();
        }
       memo.push(value);
       return value;
    };
}());
Török Gábor
Nice solution! Just beware infinite recursion in this specific example.
coderjoe
@coderjoe: thanks, I updated the code.
Török Gábor
A: 

Just implement a pseudo random algorithm, with a random seed. There are several such algorithms, some are better than others. Wikipedia has a list of algorithms.

You could use the Linear Congruential Generator (LCG), one of the best known Pseudo Random Number Generator (PRNG). The following code uses the built inn random number generator (which probably uses the LCG), and then uses LCG algorithm for the other numbers. This makes sure no two consecutive numbers are the same. There are also a range and offset, so you can limit the range of numbers you get (in this case it would be numbers from 1 to 10).

var range = 9;
var offset = 1;

function rand(x){
  var a = 1664525;
  var m = 4294967296;
  var c = 1013904223;
  return (a*x + c) % m;
}

var num1 = rand(Math.round(Math.random()*100000)) % range + offset;
var num2 = rand(num1) % range + offset;
var num3 = rand(num2) % range + offset;
Marius
While a nice solution for a PRNG, the poster doesn't want duplicate numbers in his result. While the LCG prevents sequential numbers from being duplicated, specific seeds can result in small pools of numbers being generated, resulting in duplicate numbers. For an example, try seeding rand with the value 27369, which should alternate between the number 7 and 4.
coderjoe
A: 

Judging from your comments, it sounds like you have a small range of values (i.e. from 1-10) which you want to randomly select your values from. In that case your best bet is to store your values in an array and randomly splice them out. I prefer to perform these kinds of operations using some sort of generator.

function createRandomGenerator( array ) {
 return function() {
  return array.splice(Math.floor(Math.random()  * array.length ),1)[0];
 }
}

To use the generator creator, provide it with your short list of unique values, and store the result. The result is a generator you can use to generate N number of random numbers from your list of unique values. (Where N is the length of the array you seeded your random generator with).

var random1to10 = createRandomGenerator( [1,2,3,4,5,6,7,8,9,10] );

You can then use the generator by calling the generator that that function returns over and over.

var a = [];
for( var i = 0; i < 10; i++ ) {
 a.push( random1to10() );
}
alert(a.join(','));

If you do not have a pre-defined set of numbers you wish to use, I'd recommend Török Gábor's solution above.

Cheers!

coderjoe
A: 

What is your larger goal? If you're trying to select items from a finite set in a random order, then you'll want to look at using a shuffle algorithm to randomize the ordering and then remove them in the resulting shuffled order as needed.

pimlottc