drnmslpz42661 2013-09-14 20:40
浏览 166
已采纳

在JS中生成非重复随机数

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?

  • 写回答

8条回答 默认 最新

  • dou8mwz5079 2013-09-14 21:09
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(7条)

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?