jQuery 在 JS 中生成不重复的随机数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18806210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generating non-repeating random numbers in JS
提问by Ben Hymanson
I have the following function
我有以下功能
function randomNum(max, used){
newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
console.log(newNum + " is not in array");
return newNum;
}else{
return randomNum(max,used);
}
}
Basically I am creating a random number between 1 - 10 and checking to see if that number has already been created, by adding it to an array and checking the new created number against it. I call it by adding it to a variable..
基本上,我正在创建一个介于 1 - 10 之间的随机数,并通过将其添加到数组并对照它检查新创建的数字来检查该数字是否已被创建。我通过将它添加到变量来调用它..
UPDATED:
for(var i=0;i < 10;i++){
randNum = randomNum(10, usedNums);
usedNums.push(randNum);
//do something with ranNum
}
This works, but in Chrome I get the following error:
这有效,但在 Chrome 中我收到以下错误:
Uncaught RangeError: Maximum call stack size exceeded
Which I guess it's because I am calling the function inside itself too many times. Which means my code is no good.
我猜这是因为我在内部调用函数太多次了。这意味着我的代码不好。
Can someone help me with the logic? what's a best way to make sure my numbers are not repeating?
有人可以帮我分析一下逻辑吗?确保我的数字不重复的最佳方法是什么?
回答by Ben Hymanson
If I understand right then you're just looking for a permutation (i.e. the numbers randomised with no repeats) of the numbers 1-10? Maybe try generating a randomised list of those numbers, once, at the start, and then just working your way through those?
如果我理解正确,那么您只是在寻找数字 1-10 的排列(即随机化且没有重复的数字)?也许尝试在开始时生成这些数字的随机列表,然后就按照自己的方式进行处理?
This will calculate a random permutation of the numbers in nums
:
这将计算 中数字的随机排列nums
:
var nums = [1,2,3,4,5,6,7,8,9,10],
ranNums = [],
i = nums.length,
j = 0;
while (i--) {
j = Math.floor(Math.random() * (i+1));
ranNums.push(nums[j]);
nums.splice(j,1);
}
So, for example, if you were looking for random numbers between 1 - 20 that were also even, then you could use:
因此,例如,如果您正在寻找 1 - 20 之间的随机数,并且它们也是偶数,那么您可以使用:
nums = [2,4,6,8,10,12,14,16,18,20];
Then just read through ranNums
in order to recall the random numbers.
然后通读一遍ranNums
以回忆随机数。
This runs no risk of it taking increasingly longer to find unused numbers, as you were finding in your approach.
正如您在方法中发现的那样,这不会导致查找未使用的数字所需的时间越来越长。
EDIT: After reading thisand running a test on jsperf, it seems like a much better way of doing this is a Fisher–Yates Shuffle:
编辑:阅读本文并在jsperf上运行测试后,似乎更好的方法是 Fisher–Yates Shuffle:
function shuffle(array) {
var i = array.length,
j = 0,
temp;
while (i--) {
j = Math.floor(Math.random() * (i+1));
// swap randomly chosen element with current element
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
Basically, it's more efficient by avoiding the use of 'expensive' array operations.
基本上,通过避免使用“昂贵”的数组操作,效率更高。
BONUS EDIT: Another possibility is using generators(assuming you have support):
function* shuffle(array) {
var i = array.length;
while (i--) {
yield array.splice(Math.floor(Math.random() * (i+1)), 1)[0];
}
}
Then to use:
然后使用:
var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]);
ranNums.next().value; // first random number from array
ranNums.next().value; // second random number from array
ranNums.next().value; // etc.
where ranNums.next().value
will eventually evaluate to undefined
once you've run through all the elements in the shuffled array.
一旦您遍历了混洗数组中的所有元素,ranNums.next().value
最终将评估为where undefined
。
Overall this won't be as efficient as the Fisher–Yates Shuffle because you're still splice
-ing an array. But the difference is that you're now doing that work only when you need it rather than doing it all upfront, so depending upon your use case, this might be better.
总体而言,这不会像 Fisher-Yates Shuffle 那样有效,因为您仍在splice
使用数组。但不同之处在于,您现在只在需要时才进行这项工作,而不是预先完成所有工作,因此根据您的用例,这可能会更好。
回答by shodgesio
This will do what you're looking for:
这将做你正在寻找的:
let anArrayOfUniqueNumbers = [];
let numberGenerator = function(arr) {
if (arr.length >= 10) return;
let newNumber = Math.floor(Math.random() * 10 + 1);
if (arr.indexOf(newNumber) < 0) {
arr.push(newNumber);
}
numberGenerator(arr);
};
numberGenerator(anArrayOfUniqueNumbers);
We have:
我们有:
- a new array
- a function which takes an array as an argument
- 一个新数组
- 一个将数组作为参数的函数
- Check if the array it's operating on already has ten indices, and if not:
- Generate a random number between 1-10
- If that random number isn't in the array yet, push it into the array
- Run again
- 检查它正在操作的数组是否已经有十个索引,如果没有:
- 生成 1-10 之间的随机数
- 如果该随机数不在数组中,则将其推入数组
- 再次运行
Because of the guard clause (if (arr.length >= 10) return;
), the function will stop executing once the parameters have been met.
由于保护子句 ( if (arr.length >= 10) return;
),一旦满足参数,函数将停止执行。
回答by Shahzad Harim
//random number without repetition in JavaScript, Just in one line;
//it can be used as _id;
//it not need to store or check;
const myRnId = () => parseInt(Date.now() * Math.random());
console.log(myRnId()); // any random number included timeStamp;
回答by Yamil Duba
let arr = [];
do {
let num = Math.floor(Math.random() * 10 + 1);
arr.push(num);
arr = arr.filter((item, index) => {
return arr.indexOf(item) === index
});
} while (arr.length < 10);
console.log(arr);
回答by Travis J
The issue is that as you approach saturation you begin to take longer and longer to generate a unique number "randomly". For instance, in the example you provided above the max is 10. Once the used number array contains 8 numbers it can potentially take a long time for the 9th and 10th to be found. This is probably where the maximum call stack error is being generated.
问题是,当您接近饱和时,您开始花费越来越长的时间来“随机”生成唯一数字。例如,在您上面提供的示例中,最大值为 10。一旦使用的数字数组包含 8 个数字,可能需要很长时间才能找到第 9 个和第 10 个。这可能是产生最大调用堆栈错误的地方。
jsFiddle Demo showing iteration count being maxed
jsFiddle Demo showing iteration count being maxed
By iterating inside of your recursion, you can see that a large amount of execution occurs when the array is completely saturated, but the function is called. In this scenario, the function should exit.
通过在递归内部进行迭代,您可以看到当数组完全饱和时会发生大量执行,但会调用该函数。在这种情况下,函数应该退出。
jsFiddle Demo with early break
jsFiddle Demo with early break
if( used.length >= max ) return undefined;
And one last way to accomplish both the iteration checks and the infinite recursion would be like this jsFiddle Demo
:
完成迭代检查和无限递归的最后一种方法是这样的jsFiddle Demo
:
function randomNum(max, used, calls){
if( calls == void 0 ) calls = 0;
if( calls++ > 10000 ) return undefined;
if( used.length >= max ) return undefined;
var newNum = Math.floor(Math.random() * max + 1);
if($.inArray(newNum, used) === -1){
return newNum;
}else{
return randomNum(max,used,calls);
}
}
回答by Niccolò Campolungo
function randomNumbers(max) {
function range(upTo) {
var result = [];
for(var i = 0; i < upTo; i++) result.push(i);
return result;
}
function shuffle(o){
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
var myArr = shuffle(range(max));
return function() {
return myArr.shift();
};
}
Built a little test, try this on jsfiddle:
构建了一个小测试,在jsfiddle上试试这个:
var randoms = randomNumbers(10),
rand = randoms(),
result = [];
while(rand != null) {
result.push(rand);
rand = randoms();
}
console.log(result);
Shuffle function courtesy of dzone.com.
随机播放功能由dzone.com 提供。
回答by Aaron Plocharczyk
randojs.commakes this a simple one-liner:
randojs.com使这成为一个简单的单行:
randoSequence(1, 10)
This will return an array of numbers from 1 through 10 in random order. You just need to add the following to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions as I've shown here.
这将以随机顺序返回从 1 到 10 的数字数组。您只需要将以下内容添加到 html 文档的头部,您就可以轻松地随心所欲地做任何想做的事。来自数组的随机值、随机的 jquery 元素、来自对象的随机属性,甚至像我在这里展示的那样防止重复。
<script src="https://randojs.com/1.0.0.js"></script>
回答by John
Just one solution for reference
仅供参考的一种解决方案
const fiveNums = () => {
const ranNum = () => Math.floor(Math.random() * (10 + 1));
let current;
let arr = [];
while(arr.length < 5) {
if(arr.indexOf(current = ranNum()) === -1) {
arr.push(current);
}
}
return arr;
};
fiveNums();
回答by Jonas Kl?s
In case no permutation is wanted and/or length shall be variable, here is a solution for non repeating randomized lists/arrays withoutif-statements:
如果不需要排列和/或长度应该是可变的,这里是没有if 语句的非重复随机列表/数组的解决方案:
- Shuffle function:
- Input:
- Array or object(list) of arbitrary length
- optional: last key to be filtered (Array: index number, List: String of key)
- Output:
- random Key
- to get your random item use
myArrayOrList[key]
- Input:
- 随机播放功能:
- 输入:
- 任意长度的数组或对象(列表)
- 可选:要过滤的最后一个键(数组:索引号,列表:键的字符串)
- 输出:
- 随机密钥
- 让你的随机物品使用
myArrayOrList[key]
- 输入:
// no repeat if old_key is provided
function myShuffle(arr_or_list, old_key = false) {
var keys = Array.from(Object.keys(arr_or_list)); //extracts keys
if (old_key != false) {
keys.splice(keys.indexOf(old_key), 1); // removes old_key from keys
};
var randomKey = keys[Math.floor(Math.random() * keys.length)]; // get random key
return randomKey;
}
//test:
const a = [10, 20, 30, 40, 50, 60];
const b = {
"a": 10,
"bla-bla bla": 20,
"d": 30,
"c": 40
};
var oldKeys_a = [];
var oldKeys_b = [];
oldKeys_a[0] = myShuffle(a);
oldKeys_b[0] = myShuffle(b);
var i;
for (i = 1; i < 10; i++) {
oldKeys_a[i] = myShuffle(a, oldKeys_a[i - 1]);
oldKeys_b[i] = myShuffle(b, oldKeys_b[i - 1]);
}
alert('oldKeys_a: ' + oldKeys_a + '; oldKeys_b: ' + oldKeys_b)
//random...
//>>> oldKeys_a: 1,3,0,0,5,0,4,5,2,3; oldKeys_b: d,a,d,bla-bla bla,a,c,d,bla-bla bla,a,d <<<
回答by Asuzu Kosi
while(randArr.length < SIZEOFARRAY){
val = Math.floor((Math.random() * RANGEOFVALUES));
if(randArr.indexOf(val) < 0){
randArr.push(val);
}
}
You can change SIZEOFARRAYto the size of the array you wish to use and also change RANGEOFVALUESto the range of values you wish to randomize
您可以将SIZEOFARRAY更改为您希望使用的数组的大小,也可以将RANGEOFVALUES更改为您希望随机化的值范围