Reputation: 5897
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
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
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
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
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
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
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
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