【问题标题】:a better way to do random sampling with a probability distribution? [duplicate]用概率分布进行随机抽样的更好方法? [复制]
【发布时间】:2017-07-05 01:52:49
【问题描述】:

我想从数组中选择随机项目,但具有一定的概率分布。 目前我做: myarray =[5,5,5,95] 这让我有 75% 的机会获得 5 分和 25% 的机会获得 95 分。

虽然我有更多的数字,但写出所有这些数字需要太多时间,有没有更快/更好的方法来做到这一点?

【问题讨论】:

    标签: javascript arrays random


    【解决方案1】:

    您可以拥有一个对象数组,其中包含任何 value 和一个数字形式的 weight 属性。

    // data
    const samples = [
      { value: 5, weight: 75 },
      { value: 95, weight: 25 }
    ];
    
    // requested method
    function randomSample (samples) {
      // [0..1) * sum of weight
      let sample =
        Math.random() *
        samples.reduce((sum, { weight }) => sum + weight, 0);
    
      // first sample n where sum of weight for [0..n] > sample
      const { value } = samples.find(
        ({ weight }) => (sample -= weight) < 0
      );
    
      return value;
    }
    
    // demo
    const counts = { 5: 0, 95: 0 };
    
    Array
      // take a million random samples
      .from({ length: 1000000 }, () => randomSample(samples))
      // count each sample
      .forEach(value => { counts[value]++; });
    
    console.log(counts);

    数据不必按任何特定顺序排列,权重也不必相加为任何特定总和。

    【讨论】:

      【解决方案2】:

      function weightedChoice(array, weights) {
        let s = weights.reduce((a, e) => a + e);
        let r = Math.random() * s;
        return array.find((e, i) => (r -= weights[i]) < 0);
      }
      
      let randomArray =
          Array.apply(null, Array(32)).
          map(() => weightedChoice([5, 95], [75, 25]));
      console.log(JSON.stringify(randomArray));

      编辑:帕特里克比我快一点,所以我会赞同他的回答,我只是补充一点,你并不绝对需要总和为 1,你可以通过找出总和来标准化权重自己一个人。

      编辑编辑:如果您真的担心在需要许多具有相同权重的随机值的情况下的性能,这会做得更好(通过尽可能多地预先计算):

      class WeightedSampler {
        constructor(elements, weights) {
          this.total = 0;
          this.elements = Array.from(elements);
          this.cweights = weights.map(weight => this.total += weight);
        }
        get() {
          let random = Math.random() * this.total;
          return this.elements.find((element, index) => random < this.cweights[index]);
        }
      }
      
      const sampler = new WeightedSampler(["M", "I", " "], [3, 9, 1]);
      let randomArray = Array.apply(null, Array(32)).map(() => sampler.get());
      console.log(randomArray.join(""));

      【讨论】:

      • 这也不错。我想这样做的好处是权重不必总和为 1,缺点是您必须在计算随机选择之前包含一个额外的循环来确定它们的总和。
      • @PatrickRoberts:哈哈,我只是在写那个……:D
      • 这个算法类似于this Python implementation的JS版本。
      • 顺便说一下,对于日志记录,只做Array(32).fill().map(...)
      • @PatrickRoberts 不用担心。根据您的提示,我也添加到我的答案中:D
      猜你喜欢
      • 2018-07-11
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-08
      • 1970-01-01
      相关资源
      最近更新 更多