【问题标题】:JavaScript - How to randomly sample items without replacement?JavaScript - 如何在不替换的情况下随机抽样项目?
【发布时间】:2012-10-10 20:42:55
【问题描述】:

JavaScript

我尝试过搜索类似的内容,但找不到。

这是一个简单的想法:

一个。取一个 0 到 10 之间的随机数。

b.假设滚动的随机数是 3。

c。然后,保存数字(3)。

d。现在,再取一个 0 到 10 之间的随机数,但它不可能是 3,因为它已经出现了。

【问题讨论】:

  • 问题是什么?您是否尝试过编写程序,如果是,您在哪里卡住了?因为程序的总体思路已经存在(您的子任务列表听起来不错),并且应该可以工作。
  • 您的问题没有明确定义。第一项和第二项被选中的概率是否必须相等,还是故意不同?
  • 是的,我当然试过了,网上有很多关于用JS生成随机数的代码。但我不知道如何制作 c) 和 d)

标签: javascript random probability random-sample


【解决方案1】:

一种解决方案是生成一个数组(一个“桶”),其中包含您要选择的所有值,在这种情况下,所有数字都是从 0 到 10。然后从数组中随机选择一个并将其从桶中删除。请注意,下面的示例不检查存储桶是否为空,因此如果您调用下面的函数超过 10 次,您将收到错误。

var bucket = [];

for (var i=0;i<=10;i++) {
    bucket.push(i);
}

function getRandomFromBucket() {
   var randomIndex = Math.floor(Math.random()*bucket.length);
   return bucket.splice(randomIndex, 1)[0];
}

// will pick a random number between 0 and 10, and can be called 10 times
console.log(getRandomFromBucket());

【讨论】:

  • 最优化最优雅的解决方案!
  • @skovalyov 也许是最优雅的,但不是最优化的——我上次测试时,splice 的速度大约是使用 for 循环手动改组元素的一半。如果您不需要保持数组有序,那么最快的方法是将最后一个元素移动到随机位置。
  • @Neil 响应是最佳的。简单就是大师的优雅
  • @ShawnCicoria-MSFT 我不是在抱怨优雅,但您使用分析来确定最佳解决方案,而不是简单。
【解决方案2】:

使用d3:

var bucket = d3.shuffle(d3.range(11));

while(bucket.length) {
  console.log(bucket.pop());
}

【讨论】:

    【解决方案3】:
    Var rnd = getRnd();
    
    While(rnd != lastRnd)
      rnd = getRnd();
    

    getRnd() 是一个生成随机数的函数。

    实际上,您必须检查您当前的随机数是否在数组中...如果您的可能随机数列表很小,请注意无限循环。

    【讨论】:

    • 理论上,如果你真的不走运,这可能会循环很长一段时间。
    【解决方案4】:

    你可以这样使用:

    /**
    * range Get an array of numbers within a range
    * @param min {number} Lowest number in array
    * @param max {number} Highest number in array
    * @param rand {bool} Shuffle array
    * @return {array}
    */
    range: function( min, max, rand ) {
      var arr = ( new Array( ++max - min ) )
        .join('.').split('.')
        .map(function( v,i ){ return min + i })
      return rand
        ? arr.map(function( v ) { return [ Math.random(), v ] })
           .sort().map(function( v ) { return v[ 1 ] })
        : arr
    }
    

    然后像这样使用它:

    var arr = range( 1, 10, true )
    

    现在您有一个数组,其中包含从 1 到 10 的 10 个随机顺序且从不重复的数字。所以接下来你可以这样做:

    arr.forEach(function( num, i ) { 
      // do something, it will loop 10 times 
      // and num will always be a different number
      // from 1 to 10
    });
    

    【讨论】:

      【解决方案5】:

      只是为了好玩:派生自@Strilles answer a 'bucket constructor'

      function RandomBucket(from,until){
        min = (Number(from) || 0);
        max = (Number(until) || 10)+1;
        this.bucket = String(Array(max-min)).split(',').map(function(i){
           return min++;
        });
      
        if (!RandomBucket.prototype.get){
         RandomBucket.prototype.get = function(){
            var randomValue = 
              this.bucket.length < 2
              ? this.bucket.shift()
              : this.bucket.splice(Math.floor(Math.random()*this.bucket.length),1);
             return randomValue || 'bucket empty';
            };
        }
      }
      

      用法示例见JsFiddle

      【讨论】:

        【解决方案6】:

        大多数时候我会坚持使用其他答案建议的方法 - 即创建一个可能性数组,创建一个随机版本,然后将前 n 个值作为您的样本(所有操作都简单且通用并且可以不可变地实现)。

        但是,如果与您要使用的内存量或要绘制的随机值相比,可能性的范围很大(尽管@Strilles 解决方案使用内存,但不绘制许多随机值,所以即使对于我下面的用例来说也可能是最好的)。

        您的问题似乎建议的解决方案可能如下所示:

        // select n integers from the range [from, to] (inclusive at both sides),
        // don't use this approach for large values of n
        // taking random values from the randomSource as needed
        function randomNumbersWithoutReplacement(n, from, to, randomSource = Math.random) {
            const result = [];
            for (let i = 0; i < n; ++i) {
                // i values have already been taken
                // the +1 makes it inclusive
                const rangeWidth = to - from - i + 1
        
                let value = Math.floor(rangeWidth * randomSource()) + from
        
                // correct the value compared to the already sampled integers
                for (let j = 0; j < result.length; ++j) {
                    if (result[j] <= value) {
                        value++
                    }
                }
        
                result.push(value)
        
                // sorting makes the correction loop simpler
                // (and it's nice to report the result sorted too)
                result.sort((a, b) => a - b)
            }
            return result
        }
        

        你为什么想要这个?

        const quantumLottoNumbers = randomNumbersWithoutReplacement(6, 1, 59, quantumRandomSource)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-09
          • 1970-01-01
          相关资源
          最近更新 更多