【问题标题】:choose k from n with a probability以概率从 n 中选择 k
【发布时间】:2017-11-23 14:01:48
【问题描述】:

我有一个包含 n 个元素 (e_i) 的列表。对于每一个 i,e_i 都有一个概率 p_i 被选中。

我想编写一个算法来从论文 n 中选择 k 个元素,但是我在选择它们时必须尊重每个元素的概率。我不知道该怎么做,我不知道有什么算法可以做到这一点:/

你能指导我的反思吗?

【问题讨论】:

  • Here is some overview(有限离散分布)。有简单的和不那么简单的方法,都有自己的优点和缺点。如果这不是家庭作业或学习,许多编程语言可能会在其库或非常常见的库中支持这一点(例如python: random.choice)。
  • 根据概率对元素进行排序并首先选择k ?我错过的问题还有更多吗?
  • @svasa 这意味着当采样 n-1 为 0 时采样最后一个元素的概率。但是您的方法在某种程度上与线性搜索(累积概率,然后是搜索)有关。其他算法正试图更有效地做到这一点,例如在恒定时间内(经过一些设置)。
  • 您希望始终有 k 个不同的值,还是允许某些值在所选的 k 个中多次出现?

标签: algorithm random


【解决方案1】:

必要的假设

通过期望的线性,很容易证明,如果你在n元素0, 1, 2, ..., n-1中挑选元素,使得每个元素i都有p_i被选中的概率,那么这个数字的期望挑选的元素正好是sum p_i。无论用于选择元素的算法如何,这都成立。

您正在寻找这样的算法,但添加了一个限制,即选择的元素的数量始终为k。因此,一个必要的假设是:

sum p_i = k

幸运的是,事实证明这个假设也足够了。

算法

现在假设sum p_i = k。以下算法将精确选择k 元素,这样0,1,...,n-1 中的每个元素i 都有p_i 被选中的概率。

Compute the cumulative sums:
  c_0 = 0
  c_1 = p_0
  ...
  c_i = p_0 + p_1 + ... + p_(i-1)
  ...
  c_n = k

Pick a number x uniformly at random in [0,1[

For every number y in the list x, 1+x, 2+x, 3+x, ..., k-1+x:
  Choose element i such that c_i <= y < c_(i+1)

很容易验证是否准确地选择了k 元素,并且每个元素i 都有p_i 被选择的概率。

参考

之前的算法是 80 年代或 90 年代的一篇研究论文的主题,我现在无法亲自动手;如果我能再次找到它,我将编辑这篇文章并附上参考。

【讨论】:

    【解决方案2】:

    假设您想要k distinct 元素,您可以执行以下操作:跟踪未选择元素的总remaining 概率。重复(k 次)在[0,remaining] 范围内选择一个随机数r。扫描概率,累积概率直到总和超过r。选择相应的元素。然后 -- 将remaining 减少这个概率,然后将该元素的概率归零,这样它就不会被再次选中。

    这是一个 Python 实现:

    from random import random
    
    def choose(probs,k):
        choices = []
        remaining = 1
        p = probs[:] #create a local copy
        for i in range(k):
            r = remaining * random()
            i = 0
            s = p[i]
            while s < r:
                i += 1
                s += p[i]
            choices.append(i)
            remaining -= p[i]
            p[i] = 0 #so won't be chosen again
        return choices
    
    #test:
    
    dist = [0.2, 0.4, 0.1, 0.1, 0.1, 0.05, 0.05]
    
    for i in range(10):
        print(choose(dist,4))
    

    典型输出:

    [2, 5, 1, 3]
    [1, 0, 6, 4]
    [0, 4, 1, 6]
    [1, 2, 3, 0]
    [1, 5, 2, 4]
    [3, 1, 0, 2]
    [1, 2, 0, 4]
    [1, 2, 0, 4]
    [2, 5, 1, 4]
    [1, 2, 0, 3]
    

    注意01 经常被选择,但56 相对较少。

    作为实现细节:上述算法原则上应该始终有效,但可能会出现舍入误差和与remaining 非常接近的r 值可能导致下标超出范围错误.对于某些用例,这应该很少见,您不必担心,但您可以添加错误捕获,例如在所有非零概率的总和刚好低于remaining 并且选择的r 恰好落在那个狭窄的差距中的情况下,选择具有最后一个非零概率的元素。

    【讨论】:

      【解决方案3】:

      假设您有 3 个可能的值:A, B, C 和: P(A) = 0.2, P(B) = 0.3, P(C) = 0.5。然后将累积概率放入数组p = [0.2, 0.5, 1]。在每次选择中,您将在[0, 1] 范围内生成一个随机数(使用您使用的语言的内置库)。根据该数字,您将作为响应返回大于或等于随机生成的数字的最小数字(实际上是对应于该数字 A、B 或 C 的类)。

      提示:如果使用最佳方法,该类可以在 O(logN) 时间内获得。

      这是一个例子: 如果您生成0.4 的值,那么您将返回B,因为0.5 是最小的数字&gt;= 0.4。如果您生成0.01,您将返回A

      就是这个想法,我会让你尝试实现它。如果您需要更多帮助,我也可以编写一些(伪)代码。

      【讨论】:

        【解决方案4】:

        所以元素 ix 可以表示为 (e_ix, p_ix),因为它们是它的两个组成部分。您显然已经知道要为所有这些填写哪些值。不过我会想出一个例子,所以我可以告诉你如何在不为你做的情况下做到这一点:

        (A, 1) (B, 2) (C, 3)

        您需要做的是将每个值分配给一个范围。我会做一个简单的方法,从零开始从左到右。

        所以,我们需要 1 个插槽用于 A,2 个用于 B,3 个用于 C。我们可能的索引将是 0、1、2、3、4 和 5。

        0->A
        1->B
        2->B
        3->C
        4->C
        5->C

        这是一个基本的例子,你的权重可能是浮点数,但它应该给你一个开始。

        编辑:浮点示例

        (D, 2) (E, .5123) (F, 1)

        D 2 2.5123

        【讨论】:

        • 哎哟。您可能应该在这里处理空间复杂性,尤其是当输入概率不是彼此的精确倍数时。
        • 确切的倍数无关紧要,它们与此算法无关。由于您可以在此示例中直接索引而不是检查数字是否在范围内,这使得它变得更加容易,但我只是想让 OP 了解如何包含权重。
        • 使用非整数输入时。 (很大程度上取决于这些数字的 LCM() ,这里忽略关于 LCM + 浮点数的数学错误)。您的编辑是一个很好的例子。将 0.5 与 0.35 切换。现在您将草图转换为全新的算法(累积和 + 搜索)/不再使用 bin-mapping(当有人想要自己的实现时,这实际上是我建议的算法)。
        • 你显然对此有了更正式的理解。我只是在这里求和!
        • 好的,我有点沮丧,因为我发布了一个非常相似但没有人认为有帮助的回复。这个答案有什么特别的错误吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-25
        • 1970-01-01
        相关资源
        最近更新 更多