【问题标题】:How to choose a random number in a weighted way如何以加权方式选择随机数
【发布时间】:2015-01-01 07:20:44
【问题描述】:

例如,如果我有一个这样的整数数组:

int[] list = {1, 4, 2};

我想从这 3 个数字中选择一个,但更频繁地选择更大的值:

1 get chosen 1/7 of the time
4 gets chosen 4/7 of the time
2 gets chosen 2/7 of the time

如果 Java 中还没有函数,我该如何为此编写函数。

编辑:我正在寻找一个有效的解决方案,O(n) 或更好。

我将在许多线程中多次运行此代码。建立一个新列表是不够的。

【问题讨论】:

  • 到目前为止你有没有尝试过?
  • 是的,我将每个元素复制到一个数组列表中 n 次,然后从该列表中选择一个随机元素。不过效率不高。
  • @Alexander_Winter 是的,这是我尝试过的,但我需要一个更好的主意。我觉得有一种简单的方法可以做到这一点,而无需每次都分配更多内存。
  • 您是否需要始终使用这些概率,或者每次选择随机元素时它们都会改变?

标签: java algorithm random


【解决方案1】:

累加列表中的值并调用结果和S。现在生成一个从 0 到 S - 1 的随机数。再次遍历list 并对每个元素执行以下操作:如果元素大于S,则选择该元素。否则将S 减少元素的值并继续到下一个元素。

【讨论】:

  • 值得注意的是,列表中的条目数量非常多,这可能效率低下 - 在这种情况下,您构建累积计数并在这些计数上使用二等分。 (但对于小型列表,二等分更昂贵)
  • @MichaelAnderson 您无法以比 O(N) 更好的复杂性解决此类查询的单次迭代,因为输入的量级如此之高。但是,如果您有多个查询,您可以使用二分搜索进行优化。
  • @IvayloStrandjev 我认为这是我能做到的最好的。我可以做得更好的唯一方法是,如果我最初将它们每次放入列表 n 次,然后选择 rnd 数字 1 到 n。这将是一个恒定的时间,但它是一个黑客。谢谢。
  • @Josh 请注意,您提出的解决方案不会是不变的,因为您必须事先将元素放入列表中。因此,对于单个查询,您将在计算复杂性方面表现同样出色,但需要更多内存。如果您要执行多个查询,但是您提出的解决方案可能会更好。此外,如果您要进行多个查询,您可以将我的解决方案与二分搜索结合使用(可能内存开销更少,但性能比您的解决方案差)。
  • @IvayloStrandjev 啊,我明白了。我猜想运行查询的函数将是恒定时间,但事先完成的工作也需要周期。不过,我想我会同意你上面的答案,因为每个数组只有一个查询。
【解决方案2】:

1) 取元素的总和(S)(A[i]) 2) 获取累计和列表(C[i]) 3) 获取从 0 到 S-1 的随机值(R) 4)如果R

Cumulative Sum Range    Index in the Cumulative Array      Value in Original Array  
-----------------      -----------------------------      ----------------------
 0 <= x < 1                      0                            1
 1 <= x < 6                      1                            4
 6 <= x < 7                      2                            2

【讨论】:

    【解决方案3】:

    您可以简单地使用Math.random() 获取0..1 范围内的随机数,并选择累积权重“覆盖”随机数的索引处的数字:

    public static int random(int[] numbers, double[] weights) {
        double r = Math.random();
        
        double sum = 0;
        for (int i = 0; i < weights.length; i++) {
            sum += weights[i];
            if (r < sum)
                return numbers[i];
        }
        
        return 0; // You can only get here if sum weights is less than 1
    }
    

    此解决方案会根据您在 O(N) 中提供的权重为您选择一个随机数。在大多数情况下,该算法甚至不会读取整个 weights 数组。这是最好的。最大步数为N,平均步数为N/2。

    注意事项:

    • 平均而言,如果较大的权重位于 weights 数组的开头,则该方法会更快。

    • 权重之和预计为1。如果权重之和小于 1,此方法可能返回0(概率为1-sum(weights))。

    【讨论】:

      猜你喜欢
      • 2015-07-05
      • 2010-09-08
      • 2017-12-26
      • 2022-01-08
      • 2011-05-26
      相关资源
      最近更新 更多