如何在JavaScript中Y和Z的范围之间随机生成X数量?

例如,我想在1和10之间生成5个唯一的数字。结果应该是从1到10的5个数字(例如2 3 4 8 10)。

  1. 使用您的值范围填充数组
  2. 洗牌arrays
  3. 选择前5个元素

如果范围非常大,并且您想要的值的数量非常小(例如,1 … 1000000范围内的5个不同值),那么您可以尝试生成范围内的随机数并丢弃(不太可能)重复项,直到你有5.“继续尝试”方法的问题在于,你可以花很多时间使用一串随机值来给你很多重复。 对于大范围的价值观而言,除非软件给创伤患者提供氧气,否则它可能不值得冒险。

另一种方法是产生随机数,如果它还没有包含它们,只将它们添加到返回的数组中。

function randomRange(from, to, leng){ var tem, A= [], L= 0, i= 0; randomRangeLoop: while(L< leng){ tem= Math.floor(Math.random()*to)+from; i= 0; while(i 

alert(randomRange(1,10,5))

/ *返回值:(数组)8,6,1,3,5 * /

 function generateNumbers(resultCount, from, to) { var result = []; // holds the final result var isAlreadyPicked = []; // quick lookup for the picked numbers // holds substitutes for when we pick a number that has been picked before var substitutes = []; for (var i = 0; i < resultCount; i++) { // pick a random number var number = Math.floor(Math.random() * (to - from)) + from; if(isAlreadyPicked[number]) { // pick a number from the ones we skipped at the end of the range. var j = Math.floor(Math.random() * substitutes.length); number = substitutes[j]; substitutes.splice(j, 1); } // Save the number and mark it as being picked. result.push(number); isAlreadyPicked[number] = true; // decrease the range. (Because there is 1 less number to choose from) to -= 1; if(!isAlreadyPicked[to]) { // Save a substitute for when we pick the same number in a next iteration. substitutes.push(to); } } return result; } 

它通过每次迭代减少范围的顶部来工作。 如果在结果中已经存在上一次迭代中拾取的数字,只需将其更改为我们离开该范围的顶部数字之一(之前将始终只有1个未被选中的数字)。 这也是一个真正的随机数,因为它实际上存在于前一次迭代中选取的数字的位置。

在我看来,这是最好的解决方案,因为:

  1. 它不会通过将所有数字分配给数组并对该数组进行混洗来分配大量内存。

  2. 它只有X迭代,所以把它连接到这台呼吸机:)

  3. 这是真的随机,我之前的答案,如果数字已被选中,我加1。 因为在上一次迭代中拾取的数字之后的数字比其他数字获得的几率高两倍。

编辑1:我在大量案例中测试了这个算法,我犯了一个小错误。 有一个边缘情况,有时数字不是唯一的。 它发生在该范围中的一个顶部数字已经被选中时。 我更新了我的代码。

编辑2:如果你感兴趣:这是我用来测试这个的代码:

 function test(cases, size) { var errors = 0; for (var i = 0; i < cases; i++) { var start = Math.floor(Math.random() * size); var end = start + Math.floor(Math.random() * size); var count = Math.floor(Math.random() * (end - start)); var nrs = generateNumbers(count, start, end); console.log('testing', start, end, count, nrs); test: for (var j = 0; j < count; j++) { var testedNumber = nrs[j]; if(testedNumber < start || testedNumber >= end) { console.error('out of range', testedNumber); errors += 1; break test; } for (var k = 0; k < count; k++) { if(j !== k && nrs[k] === testedNumber) { console.error('picked twice', testedNumber); errors += 1; break test; } } } } console.log('all tests finished | errors:', errors) } test(1000, 20); 

编辑3: Pointy建议使用更快的算法来查看数字是否唯一。 我用他/她的建议更新了样本。 谢谢尖尖!

编辑4:通过删除内部循环并用替换数组替换它,使算法更有效。 有趣的是,这个算法实际上可以用作前一个答案中的改组算法:)

这个问题让我很感兴趣。 我以前需要这样的算法。 这实际上是我第一次找到满足我的解决方案。 我做了一些关于这个主题的研究,这似乎是Fisher-Yates shuffle算法的一个变种。

编辑5:改变算法从替代品中挑选一个随机数而不是第一个。