【问题标题】:Uniformly and randomly choose M elements from N elements - confused从 N 个元素中均匀随机选择 M 个元素 - 困惑
【发布时间】:2015-04-03 13:39:58
【问题描述】:

我知道有人问过类似的问题

Choose m elements randomly from a vector containing n elements

Pick N items at random from sequence of unknown length

但我越看越糊涂。

从N个元素中均匀随机选择M个元素

所以我需要从 N 个元素中挑选 M 个元素。而且我还需要使每个元素被选中的概率均匀分布:M/N

我的直觉是

  1. 随机选择一个元素
  2. 拿出来
  3. 对其余元素重复该过程

我猜这个解决方案是错误的? M 被选元素的概率是 1/N, 1/(N-1), ..., 1/(N-M), not M/N, 我说对了吗?


一个可能的正确解决方案是

  1. 只需对整个 N 个元素进行随机播放
  2. 选择前 M 个。

但我无法计算出每个元素被选中的概率。

谁能证明这个解决方案的概率确实是M/N


当然我们也可以使用Reservoir sampling,它的概率是M / N

【问题讨论】:

    标签: algorithm random


    【解决方案1】:

    我认为您的困惑可能是您没有区分选择序列和选择集合。

    在您的第一个程序中,仅仅因为您在第一轮中只有1/N 机会选择特定元素,并不意味着您不会在后续轮中选择它。该元素有1/N 机会成为结果中的第一个元素......但它有M/N 机会在some 轮中被选中。所以这行得通。取 M=2,N=4:一个元素被选中的几率是1/4 + (3/4)*(1/3) = 2/4

    至于你的第二个过程,在洗牌之后,每个元素在数组中的位置是均匀分布的,所以有一个M/N 的机会,它的位置等于或小于 M(因此被选中)。所以这也行得通。

    【讨论】:

    • 我的另一个困惑是uniformly这个词。我是否希望每个元素都有1/N 被选中的机会或M/N
    • 元素A1/N 在回合i 中被选中的机会,M/N 总体任何 圆形。
    • 啊,好的。你的意思是A1/N 成为第一名的机会,第二名有1/(N-1) * (N-1)/N = 1/N,因为(N-1)/N 是它没有被选为第一名的机会。依此类推,A 有机会 M/N 成为任何选择。对吗?
    • 是的。随着轮数的增加,分数变长,但数学仍然有效。
    【解决方案2】:

    元素被选中的概率:

    First: 1/N;
    Second: 1/(N-1) * (1 - 1/N) = 1/N
    ...
    

    其中(1 - 1/N)是第二个项目在第一步没有被拾取的概率,这是它在第二步被拾取的条件。这里1/N 是在第一步中选择某个元素的概率,(1 - 1/N) - 在第二步中选择的元素在第二步中可供选择的概率。

    Third: 1/(N-2) * (1 - 1/N - 1/N) = 1/N
    

    其中(1 - 1/N - 1/N) 是元素在第三步可供选择的概率。

    等等。

    这里的重点是,对于任何元素,一步选择的概率是1/N

    【讨论】:

      【解决方案3】:

      虽然 shuffle 方法有效,但还有另一种方法不需要修改原始数组。事实上,您甚至不需要知道总共有多少项。您可以从流中随机选择 M 个项目,而您唯一需要保存的数据是这些 M 个项目的数组。

      基本思想是从未知长度的流中挑选单个项目的算法的扩展。同样,您不知道流中有多少项目。所以你总是有一个选定的项目,但你可以随时更改选定的项目。

      您从没有选定项目开始,计数为 0。当第一个项目进入时,您增加计数。然后你在 0 到 count-1 范围内选择一个随机数。如果值为 0,则使当前项成为选定项。选择的第一个随机数必须为 0,因为您的范围是 0 到 0。

      当你阅读每个项目时,你增加计数,选择​​随机数,如果选择的随机数为 0,则将当前项目设为选中项目。它看起来像这样:

      selected_item = none
      count = 0
      for each item
          count = count + 1
          if (random(count) == 0)
              selected_item = current_item
      

      这很有效,因为选择当前项目的机会随着每个项目的阅读而降低。也就是说,第一个项目以 1/1 的概率被选中。当第二个项目进来时,您有 1/2 的机会选择它来替换第一个项目。当您收到第三个项目时,您有 1/3 的机会将当前选择的项目替换为新项目。

      当您到达流的末尾时,您将给予每个项目均等的被选中机会。

      您可以很容易地将其扩展到多个项目。您首先选择进来的前 M 个项目,然后将它们放入您选择的项目数组中。然后,每当有新项目进入时,您会选择一个介于 0 和 count-1 之间的随机数,如果该数字小于 M,那么您将随机用新项目替换所选项目之一。它看起来像这样:

      selected_items = array of M items
      count = 0
      for each item
          if (count < M)
              selected_items[count] = current_item
          else
              if (random(count) < M)
                  // pick a random number from 0 to M-1
                  // and replace that item with the new item
                  ix = random(M)
                  selected_items[ix] = current_item
      

      我在我的博客Random selection 中写了更多详细信息。

      【讨论】:

      • 博文链接失效。
      猜你喜欢
      • 2012-03-09
      • 1970-01-01
      • 1970-01-01
      • 2021-07-10
      • 2018-02-22
      • 1970-01-01
      • 2010-12-18
      • 2014-06-19
      相关资源
      最近更新 更多