简体   繁体   中英

Picking 2 random elements from array

What is the most efficient way to select 2 unique random elements from an array (ie, make sure the same element is not selected twice).

I have so far:

var elem1;
var elem2;

elem1 = elemList[Math.ceil(Math.random() * elemList.length)];
do {
  elem2 = elemList[Math.ceil(Math.random() * elemList.length)];
} while(elem1 == elem2)

But this often hangs my page load.

Any better solution?

Extra question, how do I extend this to n elements

do NOT use loops and comparisons. Instead

  • shuffle the array
  • take first two elements

http://underscorejs.org/#sample

_.sample(list, [n])

Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned.

_.sample([1, 2, 3, 4, 5, 6]);
=> 4

_.sample([1, 2, 3, 4, 5, 6], 3);
=> [1, 6, 2]

Looking at the source it uses shuffle just like @thg435 suggested.

Your code will hang when the list contains only one item. Instead of using == , I recommend to use === , which looks more suitable in this case.

Also, use Math.floor instead of Math.ceil . The length property is equal to <highest index> + 1 .

var elem1;
var elem2;
var elemListLength = elemList.length;

elem1 = elemList[Math.floor(Math.random() * elemListLength)];
if (elemListLength > 1) {
    do {
      elem2 = elemList[Math.floor(Math.random() * elemListLength)];
    } while(elem1 == elem2);
}

It can be done using built-in functionality ( slice and sort ),

var n = 2
    randomItems = array.sort(() => .5 - Math.random()).slice(0, n);

On what Rob W told you, I'll add that a different solution would be to find a random point and for the second point find a random offset from the point:

var elem1;
var elem2;
var elemListLength = elemList.length;

var ix = Math.floor(Math.random() * elemListLength);
elem1 = elemList[ix];

if (elemListLength > 1) {
    elem2 = elemList[(ix + 1 + Math.floor(Math.random() * (elemListLength - 1))) % elemListLength];
}

We add 1 because the current element can't be reselected and subtract 1 because one element has already been selected.

For example, an array of three elements (0, 1, 2). We randomly select the element 1. Now the "good" offset value are 0 and 1, with offset 0 giving the element 2 and offset 1 giving the element 0.

Note that this will give you two random elements with different INDEX, not with different VALUE!

如果您想获得n随机元素,您可以创建列表的混洗版本,然后返回混洗数组的前n元素。

While shuffle the array and pick the first two is correct.
You don't need to shuffle the whole array.

Just shuffle the first two!

var arrElm = [1, 2, 3, 4, 5, 6, 7]

var toTake = 2

var maxToShuffle = Math.min(arrElm.length - 1, toTake)

for (let i = 0; i < maxToShuffle; i++) {
  const toSwap = i + Math.floor(Math.random() * (arrElm.length - i))
  ;[arrElm[i], arrElm[toSwap]] = [arrElm[toSwap], arrElm[i]]
}

console.log(arrElm.slice(0, toTake))

basically the same as https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

Except you just quit early when you have enough item shuffled.

You can do something easy like this

const elements = ['indie hackers', 'twitter', 'product hunt', 'linkedIn'];
const randomIndex = Math.floor(Math.random() * elements.length);
const a = elements[randomIndex];

const filteredElements = [...elements].splice(randomIndex, 1);
const b = filteredElements[Math.floor(Math.random() * elements.length)];

a and b will be your random elements.

I find this to be one of the most useful techniques:

var index1 = Math.floor(Math.random() * array.length);
var index2 = Math.floor(Math.random() * (array.length-1));
if index1 == index2 {
   index2 += 1;
}

You cannot go out of bounds as index2 cannot get the last element.

If you shuffle the array and splice the number of elements you want to return, the return value will contain as many items as it can, if you ask for more items than are in the array. You can shuffle the actual array or a copy, with slice().

Array.prototype.getRandom= function(num, cut){
    var A= cut? this:this.slice(0);
    A.sort(function(){
        return .5-Math.random();
    });
    return A.splice(0, num);
}
var a1= [1, 2, 3, 4, 5];
a1.getRandom(2)
>>[4, 2]

If you want to remove the selected items from the original array, so that a second call will not include the elements the first call returned, pass a second argument: getRandom(3,true);

window.Memry=window.Memry || {};
Memry.a1= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

Memry.a1.getRandom(3,true);
>>[5,10,7]
Memry.a1.getRandom(3,true);
>>[3,9,6]
Memry.a1.getRandom(3,true);
>>[8,4,1]
Memry.a1.getRandom(3,true);
>>[2]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM