【问题标题】:Choose elements based on odds without going through entire list to compute the probability根据赔率选择元素,无需遍历整个列表来计算概率
【发布时间】:2014-07-06 01:59:39
【问题描述】:

假设我们有一个包含一些对象的数组,每个对象都有一定的权重,表示其被选中与其他元素的比率。因此,例如,如果您有一个重量为 10 的物体、一个重量为 30 和一个重量为 35 的物体,则比例为 10:30:35。第一个被选中的概率应该是10/75 = 13.3%,第二个被选中的概率应该是30/75 = 40%,第三个被选中的概率应该是35/75 = 46.6%

现在,假设我们有一个函数,它给定了这个数组,并且必须根据其权重返回一个随机选择的对象。蛮力做到这一点的方法是用 for 循环遍历每个对象并将其权重添加到某个总数中,然后再次遍历循环并查看是否应根据某个随机概率函数选择每个对象:

int totalWeight = 0;

for(Object o: array) {
  totalWeight += o.weight;
}

//Now we have the total weight
for(Object o in array) {
  /* randomProb(double x) is some function which only returns true x percent of the time */
  if(randomProb(o.weight / totalWeight)) {
    return o;
  }
}

但是假设我们要处理数千个输入,因此对每个输入进行两次迭代是成本密集型的​​。是否有更简单的算法来确定返回哪个对象,而不必遍历循环一次以找到总权重,然后在每个元素上再次遍历它?

【问题讨论】:

  • 您是否以相同的权重多次处理同一组输入?
  • 输入包含可能具有不同值的不同字段。这些值大多在一个很小的范围内,因此可能有一些输入除了权重之外是相同的。
  • 但我的问题是您是否尝试为同一组权重获取多个随机选项 - 在这种情况下,您可以做一些“准备工作”,然后每次都使用它来有效地选择。
  • 猜猜我误解了你在问什么。不,权重是随机的,但在同一范围内。
  • 我还是不明白你的意思......这不是关于单个范围,而是你有一组权重,然后用于获取多个随机值?或者你每次都会有一组不同的权重?

标签: java statistics probability


【解决方案1】:

是的,您可以一次性完成,但您需要对n - 1 统一随机变量进行采样,其中n 是列表的长度。在不了解有关您的应用程序的更多详细信息的情况下,我们不能说这是否真的更好。

我猜想切换到一次性算法通常不会提供太多的速度优势,但如果您的列表太长而无法放入内存,则可能会很有用。

算法(请注意,我使用的是 1 相对索引),用于包含元素 x[1], x[2], ..., x[n]、正权重 w[1], w[2], ..., w[n] 的列表。

伪代码(抱歉,不是在 java 中,但你应该可以转换它):

x = x[1]
w_total = w[1]

for i in 2..n
    w_total = w_total + w[i]
    if (SampleUniform(0, 1) < w[i] / w_total)
       x = x[i]

我假设SampleUniform(0, 1) 对连续均匀 (0, 1) 随机变量进行采样。 for 循环完成后,x 现在是列表 x[1], x[2], ..., x[n] 中具有所需属性的样本。具体

Probability(x = x[i]) = w[i] / w_total

在哪里

w_total = Sum(j = 1..n) w[j].

通过检查for 循环中发生的任意i 并使用归纳证明,可以相对简单地了解其工作原理。假设当前x = x[j] 已经从x[1], ..., x[i - 1] 列表中以概率w[j] / Sum(k = 1 to i - 1) w[k] 采样(我们将在下一节中处理这个问题)。新的w_total 将等于Sum(k = 1 to i) w[i] 并且应该清楚的是,在对均匀随机变量进行采样后,我们将拥有

Probability(x = x[i]) = w[i] / w_total

Probability(x = x[j]) = w[j] / (Sum(k = 1 to i - 1) w[k]) * 
                        Sum(k = 1 to i - 1) w[k] / w_total 
                      = w[j] / w_total.

由于ji 都是任意的,它认为在for 循环中的每一步之后,x 将满足该属性

Probability(x = x[k]) = w[k] / w_total for k in 1 ... i.

应该很容易看出这种方法对于单个元素的列表是正确的(这解决了上面的“假设”语句)。因此通过归纳,在 for 循环之后

Probability(x = x[i]) = w[i] / w_total

在哪里

w_total = Sum(j = 1..n) w[j].

(因此算法有效)。

话虽如此,我不知道这对于适合内存的列表是否是一个好主意(快速)。我对此表示怀疑,采样n - 1 统一随机数可能比第二次循环遍历列表需要更多时间(您可以进行一些速度测试来检查这一点)。

【讨论】:

    猜你喜欢
    • 2014-12-15
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多