部分随机检索,部分最小化重复

时间:2017-09-21 22:08:01

标签: javascript algorithm random

我有一个包含10个数字的数组:

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

我编写了一个函数(下面),从数组中返回一个随机元素,而不重复返回相同的元素,直到返回了8个其他元素。它工作但似乎非常低效,因为在代码可以继续之前,通常需要多次调用随机数生成器。有没有更有效的方法来实现相同的结果?

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var recentlyReturned = [];

function minimalRepeat() {

var i = Math.floor(Math.random() * 10);
while( recentlyReturned.indexOf(i) != -1 ) {
    i = Math.floor(Math.random() * 10);
}

recentlyReturned.push(i);
while(recentlyReturned.length>8) {
    recentlyReturned.shift();
}
return array[i];
}


for(var i=1; i<20; i++) {
    console.log(minimalRepeat());
}

*编辑*

我根据Tudor Ilisoi的反馈改写了代码。它肯定更有效率。但是,可以跨越随机边界重复。

第2版:

function shuffle(arr) {
    var copyArr = arr.slice();
    for (let i = copyArr.length; i; i--) {
        let j = Math.floor(Math.random() * i);
        [copyArr[i - 1], copyArr[j]] = [copyArr[j], copyArr[i - 1]];
    }
    return copyArr;
}

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var b = shuffle(a);

for(var i=1; i<20; i++) {
    if(b.length == 0) {
        b = shuffle(a);
    }
    console.log(b.pop());
}

3 个答案:

答案 0 :(得分:0)

你可以这样做:

  • 制作数组的副本并将其随机化。我们称之为copy
  • 您的minimalRepeat将从copy.pop()的随机副本中提取,直到副本为空(或者提取了8个元素)。满足此条件时,请另外制作副本

通过从随机副本中提取,您确保在再次生成副本之前不会返回相同的元素。

根据评论反馈进行修改

&#13;
&#13;
function shuffle(arr) {
  var copyArr = arr.slice();
  for (let i = copyArr.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [copyArr[i - 1], copyArr[j]] = [copyArr[j], copyArr[i - 1]];
  }
  return copyArr;
}

var latest = []
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var b = shuffle(a);

for (var i = 1; i < 20; i++) {
  if (b.length == 0) {
    b = shuffle(a);
  }
  var popped = b.pop()

  //if found in recents
  if (latest.indexOf(popped) !== -1) {
    continue;
  }

  //push the popped element
  latest.push(popped)
  if (latest.length > 8) {
    latest.shift() //remove the oldest "recent" to keep the "latest" length at 8
  }

  console.log(popped);
}
&#13;
&#13;
&#13;

答案 1 :(得分:0)

费 - 耶茨:

function shuffle(array) {
  var m = array.length, t, i;

  while (m) {

    i = Math.floor(Math.random() * m--);

    t = array[m];
    array[m] = array[i];
    array[i] = t;
  }

  return array;
}

var origArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var numbers = origArray;
var randomNumbers = shuffle(numbers);

var result = randomNumbers.slice(0, 8);

for (var i = 0; i < 11; i++) {
  result.push(randomNumbers[Math.floor(Math.random() * (origArray.length-1))]);
}

前8个数字应该是唯一的(来自预设集) 接下来的11个数字可以是所选数字和预定义集合中的重复数字。

(如果我正确理解了这个问题。)

根据评论进行修改

由于 no-repeat-window 为8,因此您将从阵列中留下两个数字;最后两个数字。

下一个随机数是数组中的第一个数字。

您可以将popshift这些数字放入一个临时数组中,将临时数组和push临时数组混淆回随机数组。

var origArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var randomNumbers = shuffle(origArray);

for (var i = 0; i < 19; i++)
{
    var tmp = [];
    tmp.push(randomNumbers.pop());   // First number not in no-repeat-window
    tmp.push(randomNumbers.pop());   // Second number not in no-repeat-window
    tmp.push(randomNumbers.shift()); // Current random number.

    randomNumbers.push.apply(randomNumbers, shuffle(tmp));
}

jsFiddle

答案 2 :(得分:0)

您可以在数组中组织循环队列:

Randomize array A[] with Fisher-Yates algo
Set Idx to 0
At every step generate random r = 0 or 1 
If r=1 swap A[Idx % 10] and A[(Idx + 1) % 10]
Output A[Idx % 10] 
Increment Idx