tristantzara
tristantzara

Reputation: 5897

Decrease the probability of getting random item from array same as previous one

Say, I've got an array like

var arr = [1,2,3,4,5,6,7,8,9,10,11,12];

and I wanna get random array item, but later I would like to re-randomize my current item. What is the efficient way to exclude or reduce the chance of getting the same item once again?

Does stuff like this really help:

current != arr[Math.floor(Math.random() * 12)] ? current = arr[Math.floor(Math.random() * 12)] : arr[Math.floor(Math.random() * 12)];

I mean, would it recalculate random array index each time or just link to the same value? What is a better way?

Upvotes: 3

Views: 1052

Answers (7)

Amit
Amit

Reputation: 46323

You can do this with a single calculation:

// Just like before...
var arr = [1,2,3,4,5,6,7,8,9,10,11,12];
// Initialize first random index
var currentIndex = Math.floor(Math.random() * arr.length);

// And now...
function Next() {
     currentIndex = (Math.floor(Math.random() * (arr.length - 1)) +
       1 + currentIndex) % arr.length;
     return currentIndex;
}

// Or if you want the next value...
function NextValue() {
     return arr[Next()];
}

The idea is that you always randomize how many items to advance, with a maximum of (length - 1), and use modulo to truncate the index into the valid range.

Upvotes: 0

weaknespase
weaknespase

Reputation: 1034

If you can keep array unsorted: (if not, you can use array which only contains indices of elements in first array)

var array = [ ... ];
var len = array.length;

function getRandomItem(){
    if (len <= 0) len = array.length;
    var item = Math.floor(Math.random()*len--);
    var x = array[item];
    array[item] = array[len];
    array[len] = x;
    return array[len];
}

Idea behind is to exclude already dispatched items by placing them outside of item fetching range. Function getRandomItem() will not return same item twice until all other elements also will be returned.


Following modification will only prevent function to return same element which was returned during previous call, as requested.

var array = [ 3, 1, 4, 5, 9, 2, 6, 8, 7 ];
var len = array.length-1;

function getRandomItem(){
    if (len == 0) return array[0];
    var item = Math.floor(Math.random()*len);
    var x = array[item];
    array[item] = array[len];
    array[len] = x;
    return array[len];
}

document.write("Starting array: " + array + "<br/>");
document.write("Selected value: " + getRandomItem() + "<br/>");
document.write("Resulting array: " + array);

Also see Fisher–Yates shuffle

Upvotes: 2

Moob
Moob

Reputation: 16184

You could randomly shuffle the array using a version of the Fisher-Yates algorithm and then just iterate through it:

var arr = [1,2,3,4,5,6,7,8,9,10,11,12],
    shuffle = function(array) {
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return array;
    },
    randomisedArray = shuffle(arr),
    nextItem = function(){
      return randomisedArray.pop();
    };

while(randomisedArray.length>0){
   console.log(nextItem());
}

Upvotes: 0

tristantzara
tristantzara

Reputation: 5897

perhaps this is ok? It seems to work, but maybe I'm missing some details?

            var current = arr[Math.floor(Math.random() * 12)];
            var prev = current;
            do { current = arr[Math.floor(Math.random() * 12)]; }
            while (current == prev);

Upvotes: 0

guest271314
guest271314

Reputation: 1

Try

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

var clone = arr.slice();

var res = [];

do {
  res.push(clone.splice(Math.random() * clone.length, 1)[0])
} while (clone.length !== 0);

// do stuff with res
document.write(JSON.stringify(res));

document.write(res[Math.floor(Math.random() * res.length)]);

console.log(arr);

Upvotes: 0

mbillard
mbillard

Reputation: 38842

There are so many ways to achieve this. Maybe add a weight to each value and consider it in your random number selection? Then when you get it, reduce its weight. For example:

var weights = {};
var max = 12;

function initializeWeights() {
    var i;

    for (i = 1; i <= max; ++i) {
        weights[i] = 100;
    }
}

function getPseudoRandom() {
    var possible_values = [], i, j;

    for (i = 1; i <= max; ++i) {
        for (j = 0; j < weights[i]; ++j) {
            possible_values.push(i);
        }
    }

    random_index = Math.floor(Math.random() * possible_values.length) + 1;
    random = possible_values[random_index];
    weights[random] = weights[random] - 10;
    return random;
}

initializeWeights()
alert(getPseudoRandom());

Then you just have to figure out what to do when you reach 0. Maybe you can increment all the weights by 100.

Upvotes: 0

Mostafa Talebi
Mostafa Talebi

Reputation: 9173

I think the best solution is to put a while loop to check if the value is similar to the previous one or not.

var arr = [1,2,3,4,5,6,7,8,9,10,11,12];

Then you write:

   var last_generated_value = null;
   var val = Math.random();
   val = Math.floor(val * 12);

   while(last_generated_val == val)
   {
       val = Math.random();
       val = Math.floor(val * 12);
   }

   last_generated_value = val;

Though you may put the above code block in a parent loop or a function to generate a concatenated set of values(in your case, number).

Upvotes: 1

Related Questions