在JS中生成非重复随机数

时间:2013-09-14 20:40:57

标签: javascript jquery

我有以下功能

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

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

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

18 个答案:

答案 0 :(得分:24)

如果我理解的话,你只是在寻找数字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以便回忆随机数。

正如您在方法中所发现的那样,不会花费更长的时间来查找未使用的数字。

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

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

BONUS EDIT :另一种可能是使用generators(假设您有support):

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最终将评估为undefined

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

答案 1 :(得分:2)

试试这个:

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(" - "));

答案 2 :(得分:2)

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

答案 3 :(得分:1)

const GenerateRandomNumbers = (max) => {
let orderNumbers = new Set();
for (let i = 1;  ;i++){
    let random = Math.floor(Math.random() * max + 1)  ;
    orderNumbers.add(random);
    
    if (orderNumbers.size == max){
        break;
    }
    
    }
    return orderNumbers;}

答案 4 :(得分:1)

这将满足您的需求:

//companion object
object MyModel {
  implicit def myModelOptOptDecoder[A](implicit d: Decoder[A]): Decoder[Option[Option[A]]] = 
    MyHelperObject.optOptDecoder
  implicit val myModelDecoder: Decoder[MyModel] = deriveDecoder
}

我们有:

  • 一个新数组
  • 以数组作为参数的函数
该功能将:
  • 检查其操作的数组是否已经有十个索引,如果没有,则:
  • 生成1-10之间的随机数
  • 如果该随机数还不在数组中,请将其推入数组
  • 再次运行

由于保护子句( <form asp-action="myActionMethod" method="post"> <h3>Do you like pizza?</h3> <div class="checkbox"> <label> <input asp-for="likesPizza"/> Yes </label> </div> </form> ),一旦满足参数,该函数将停止执行。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

问题在于,当您接近饱和度时,您会开始花费更长时间来“随机”生成唯一数字。例如,在上面提供的示例中,max是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);
 }
}

答案 7 :(得分:0)

randojs.com使它成为简单的单行代码:

randoSequence(1, 10)

这将以随机顺序返回从1到10的数字数组。 您只需要在html文档的开头添加以下内容,就可以轻松随机地完成几乎所有您想做的事情。数组中的随机值,随机jquery元素,对象中的随机属性,甚至可以防止重复,如我在此处所示。

<script src="https://randojs.com/1.0.0.js"></script>

答案 8 :(得分:0)

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

DEMO

答案 9 :(得分:0)

Future<Configure> conf = getLatestVersion(request);
conf.onComplete( res -> {
  if( res.succeded() ){
    Configure cc = res.result();
    float previousVersion = cc.getVersionNo();
  }
} );

答案 10 :(得分:0)

while(randArr.length < SIZEOFARRAY){
  val = Math.floor((Math.random() * RANGEOFVALUES));

  if(randArr.indexOf(val) < 0){
    randArr.push(val);
  }
}

您可以将 SIZEOFARRAY 更改为要使用的数组的大小 并将 RANGEOFVALUES 更改为您希望随机化的值范围

答案 11 :(得分:0)

如果不需要排列和/或长度是可变的,这是非重复的随机列表/数组的一种解决方案,没有 if语句:

  1. 随机播放功能:
    • 输入:
      • 任意长度的数组或对象(列表)
      • 可选:要过滤的最后一个键(数组:索引号,列表:键的字符串)
    • 输出:
      • 随机密钥
      • 使用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 <<<

答案 12 :(得分:0)

仅供参考的解决方案

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

答案 13 :(得分:0)

对不起,这是一个旧问题的新答案,但这可以通过地图更有效地完成。你所追求的是随机选择而不是非重复随机。非重复随机是荒谬的。

其中_a是集合,而r不是集合的一部分,我们lambda随机值r:

function aRandom(f){
  var r = Math.random();
  aRandom._a[r] ? aRandom(f) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};

//usage:
aRandom(function(r){ console.log(r) });

当浏览器变得缓慢时重新定义aRandom._a。为了避免最终的迟缓,人们应该真正使用具有足够熵的UUID生成算法,以便重复的机会实际上为零,而不是暴力强制可分性。我选择了函数名称aRandom,因为拉丁语前缀A-表示“远离”。由于使用的越多,输出越远离随机。该功能在Macbook上在2100毫秒内产生一百万个唯一值。

上述解决方案的优点是不需要限制该组。同样,多个呼叫者可以同时使用它,并假设它们的值与所有其他呼叫者不同。这对于诸如保险没有重叠的噪声抖动分布这样的事情是很方便的。

但是,它也可以修改为返回整数,以便将ram使用限制为所提供的长度:

function aRandom(f,c){
  var r = Math.floor(Math.random()*c);
  aRandom._a[r] ? aRandom(f,c) : f(r,aRandom._a[r] = 1);
}
aRandom._a = {};


//usage:
var len = 10;
var resultset = [];
for(var i =0; i< len; i++){
  aRandom(function(r){ resultset.push(r); }, len);
}
console.log(resultset);

答案 14 :(得分:0)

AND ((@ExcludeCB=1 and tn.tt !='CB') or @ExcludeCB=0)

答案 15 :(得分:0)

这是我使用underscore.js

实现的方法

nmin值获取max个整数。其中nsize参数。

var randomNonRepeatingIntFromInterval = function(min, max, size) {
    var values = [];

    while (values.length < size) {
      values.push(Math.floor(Math.random() * ( max - min + 1) + min));

      values = _.uniq(values);
    }

    return values;
  }

答案 16 :(得分:0)

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

构建一个小测试,在jsfiddle上尝试:

var randoms = randomNumbers(10),
    rand = randoms(),
    result = [];
while(rand != null) {
    result.push(rand);
    rand = randoms();
}
console.log(result);

随机播放功能由dzone.com提供。

答案 17 :(得分:-2)

你真的不想丢失随机数字。真正的随机数必须能够重复。

真正的随机数就像掷骰子一样。接下来会出现任何数字。

随机播放的数字就像绘制扑克牌一样。每个号码只能出现一次。

你真正要求的是随机播放一个数字列表,然后使用洗牌列表中的第一个这么多数字。

考虑按顺序制作数字列表,然后使用随机数生成器从该列表的副本中随机选择一个数字。每次都将所选数字放在新列表的末尾,并将其从旧列表的副本中删除,从而缩短该列表。完成后,新列表将包含随机数字,旧列表的副本将为空。

或者,您可以选择所选的数字并立即使用它,通过删除使用的数字来缩短列表的副本。由于您已从列表中删除了该号码,因此无法再次显示该号码。