我理解类似的问题就像
一样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
我的直觉是
我猜这个解决方案错了? M
所选元素的概率为1/N
,1/(N-1)
,...,1/(N-M)
,不 M/N
,我是对的吗?
可能正确的解决方案是
但我无法弄清楚每个元素被选中的概率。
任何人都可以证明此解决方案的概率确实为M/N
吗?
当然,我们也可以使用Reservoir sampling,其概率为M / N
。
答案 0 :(得分:3)
我认为你的困惑可能是你在选择一个序列和选择一个序列之间没有区别。
在您的第一个程序中,仅仅因为您在第一轮中只有1/N
次机会选择特定元素,并不意味着您不会在后续轮次中选择它。该元素有1/N
成为结果中第一个元素的机会......但在某些轮期间它有M/N
的机会被选中。这样才行。取M = 2,N = 4:元素被挑选的几率为1/4 + (3/4)*(1/3) = 2/4
。
至于你的第二个程序,在洗牌之后,阵列中每个元素的位置是均匀分布的,因此M/N
的位置等于或小于M({1}}因此被选中)。所以这也有效。
答案 1 :(得分:3)
挑选元素的概率:
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
。
答案 2 :(得分:1)
虽然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/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中写了更多详细信息。