在 JS 中生成不重复的随机数

IT技术 javascript jquery
2021-01-14 08:33:54

我有以下功能

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);
  }
}

基本上,我正在创建一个介于 1 - 10 之间的随机数,并通过将其添加到数组并对照它检查新创建的数字来检查该数字是否已创建。我通过将它添加到变量来调用它..

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

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

   //do something with ranNum
}

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

Uncaught RangeError: Maximum call stack size exceeded

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

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

6个回答

如果我理解正确,那么您只是在寻找数字 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以回忆随机数。

正如您在方法中发现的那样,这不会导致查找未使用的数字所需的时间越来越长。

编辑:阅读本文并在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]);

基本上,通过避免使用“昂贵”的数组操作,效率更高。

奖励编辑:另一种可能性是使用生成器(假设您有支持):

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;    // first random number from array
ranNums.next().value;    // second random number from array
ranNums.next().value;    // etc.

一旦您遍历了混洗数组中的所有元素,ranNums.next().value最终将评估为where undefined

总体而言,这不会像 Fisher-Yates Shuffle 那样有效,因为您仍在splice使用数组。但不同之处在于,您现在只在需要时才进行这项工作,而不是预先完成所有工作,因此根据您的用例,这可能会更好。

//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;

我喜欢你的想法,但我认为它可能在某些时候匹配,为了更安全,我会使用 const myRnId = (deepness = 10)=> parseInt(Date.now() + Math.random()*deepness)
2021-04-07 08:33:54

试试这个:

var numbers = []; // new empty array

var min, max, r, n, p;

min = 1;
max = 50;
r = 5; // how many numbers you want to extract

for (let i = 0; i < r; i++) {
  do {
    n = Math.floor(Math.random() * (max - min + 1)) + min;
    p = numbers.includes(n);
    if(!p){
      numbers.push(n);
    }
  }
  while(p);
}

console.log(numbers.join(" - "));

HTML

<p id="array_number" style="font-size: 25px; text-align: center;"></p>

JS

var min = 1;
var max = 90;
var stop = 6;  //Number of numbers to extract

var numbers = [];

for (let i = 0; i < stop; i++) {
  var n =  Math.floor(Math.random() * max) + min;
  var check = numbers.includes(n);

if(check === false) {
  numbers.push(n);
} else {
  while(check === true){
    n = Math.floor(Math.random() * max) + min;
    check = numbers.includes(n);
      if(check === false){
        numbers.push(n);
      }
    }
  }
}

sort();

 //Sort the array in ascending order
 function sort() {
   numbers.sort(function(a, b){return a-b});
   document.getElementById("array_number").innerHTML = numbers.join(" - ");
}

演示

嘿,欢迎来到 StackOverflow。感谢您的回答,但请记住,仅依赖代码的回答通常被认为是低质量的。下次尝试添加一些解释!
2021-03-23 08:33:54

问题是,当您接近饱和时,您开始花费越来越长的时间来“随机”生成唯一数字。例如,在您上面提供的示例中,最大值为 10。一旦使用的数字数组包含 8 个数字,可能需要很长时间才能找到第 9 个和第 10 个。这可能是产生最大调用堆栈错误的地方。

jsFiddle Demo showing iteration count being maxed

通过在递归内部进行迭代,您可以看到当数组完全饱和时会发生大量执行,但会调用该函数。在这种情况下,函数应该退出。

jsFiddle Demo with early break

if( used.length >= max ) return undefined;

完成迭代检查和无限递归的最后一种方法是这样的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);
 }
}
@gdaniel - 查看编辑和演示。基本上,您应该防止无限递归或大量未命中,以防最大值用于最大值。
2021-03-24 08:33:54
对的,我明白了。那么实现它的最佳方法是什么?
2021-04-03 08:33:54