在JS中生成非重复随机数 [英] Generating non-repeating random numbers in JS

查看:140
本文介绍了在JS中生成非重复随机数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下功能

  function randomNum(max,used){
newNum = Math.floor( Math.random()* max + 1);

if($。inArray(newNum,used)=== -1){
console.log(newNum +不在数组中);
返回newNum;

} else {
return randomNum(max,used);
}
}

基本上我创建的是1到10之间的随机数并通过将其添加到数组并检查新创建的数字来检查是否已创建该数字。我通过将其添加到变量来调用它。

 更新:
for(var i = 0; i< ; 10; i ++){

randNum = randomNum(10,usedNums);
usedNums.push(randNum);

//用ranNum做一些事情
}

这个有效,但在Chrome中我收到以下错误:

 未捕获RangeError:最大调用堆栈大小超过

我猜这是因为我在内部调用函数的次数太多了。这意味着我的代码不好。



有人可以帮我逻辑吗?什么是确保我的数字不重复的最好方法?

解决方案

如果我理解正确那么你只是在寻找一个数字1-10的置换(即无重复的随机数)?
也许尝试生成这些数字的随机列表,一次,在开始时,然后只是通过那些?



这将计算随机排列 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);
}

因此,例如,如果您要查找1到20之间的随机数那也是偶数,那么你可以使用:

  nums = [2,4,6,8,10,12,14 ,16,18,20]。 

然后只需阅读 ranNums 以便回想一下随机数字。



这样就不会有越来越长的时间来找到未使用的数字,正如你在方法中所发现的那样。



编辑:阅读并对<运行测试< a href =http://jsperf.com/shuffles> jsperf ,看起来更好的做法是Fisher-Yates Shuffle:

  function shuffle(array){
var i = array.length,
j = 0,
temp;

while(i - ){

j = Math.floor(Math.random()*(i + 1));

//用当前元素交换随机选择的元素
temp = array [i];
array [i] = array [j];
array [j] = temp;

}

返回数组;
}

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

基本上,通过避免使用昂贵的阵列操作来提高效率。



'p>的奖金修改:另一种可能性是使用发电机(假设有支持):

  function * shuffle(array){

var i = array.length;

while(i - ){
yield array.splice(Math.floor(Math.random()*(i + 1)),1)[0];
}

}

然后使用:

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

ranNums.next()。value; //数组中的第一个随机数
ranNums.next()。value; //数组中的第二个随机数
ranNums.next()。value; //等

其中 ranNums.next()。value 最终将评估为 undefined



总的来说,这不会像Fisher-Yates Shuffle那样有效,因为你还是 splice -ing a array。但不同之处在于,您现在只在需要时才开展这项工作,而不是提前完成,因此根据您的使用情况,这可能会更好。


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..

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:

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?

解决方案

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?

This will calculate a random permutation of the numbers in 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:

nums = [2,4,6,8,10,12,14,16,18,20];

Then just read through ranNums in order to recall the random numbers.

This runs no risk of it taking increasingly longer to find unused numbers, as you were finding in your approach.

EDIT: After reading this and running a test on jsperf, it seems like a much better way of doing this is a 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.

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.

这篇关于在JS中生成非重复随机数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆