例如,我想在1&之间生成5个唯一数字。 10.结果应该是1到10的5个数字(例如2 3 4 8 10)。
答案 0 :(得分:6)
如果范围非常大,并且您想要的值的数量非常小(例如,1 ... 1000000范围内的5个不同值),那么您可以尝试在范围内生成随机数并扔掉(不太可能重复,直到你有5.“继续尝试”的方法的问题是,你可以花很多时间使用一串随机值,给你很多重复。对于大范围的价值观而言,除非软件给创伤患者提供氧气,否则它可能不值得冒险。
答案 1 :(得分:3)
另一种方法是生成随机数并仅将其添加到 返回的数组,如果它还没有包含它们。
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<L){
if(A[i++]=== tem) continue randomRangeLoop;
}
A[L++]= tem;
}
return A;
}
警告(randomRange(1,10,5))
/ *返回值:(数组) 8,6,1,3,5 * /
答案 2 :(得分:2)
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个未被选中的数字)。这也是一个真正的随机数,因为它实际上存在于前一次迭代中选取的数字的位置。
在我看来,这是最好的 解决方案因为:
它没有分配大量内存 通过将所有数字分配给数组 并洗牌那个数组。
它只有X次迭代所以挂钩它 直到这台呼吸机:)
这是真的随机,我以前 回答我在哪里添加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建议使用更快的算法来查看数字是否唯一。我用他/她的建议更新了样本。谢谢Pointy!
编辑4:通过删除内部循环并将其替换为替换数组,使算法更有效。有趣的是,这个算法实际上可以用作前一个答案中的改组算法:)
这个问题让我很感兴趣。我以前需要这样的算法。这实际上是我第一次找到满足我的解决方案。我对这个主题进行了一些研究,这似乎是Fisher-Yates shuffle算法的变体。
编辑5:更改算法以从替代品中挑选一个随机数而不是第一个。