虽然洗牌方法可行,但还有一种不需要修改原始数组的方法。实际上,您甚至不需要知道总共有多少项。您可以从流中随机选择M个项目,而您唯一需要保存的数据就是这些M个项目的数组。
基本思想是从未知长度的流中选择单个项目的算法的扩展。同样,您不知道流中有多少项。因此,您始终有一个选定的项目,但您可能随时更改所选的项目。
您开始没有选择的项目,并且计数为0。当第一个项目进来时,您增加计数。然后,您在范围0到计数-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到计数-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
我在我的博客《随机选择》中详细地阐述了这一点。
1/N
的选择机会还是M/N
的选择机会? - Jackson TaleA
在第i
轮中被选中的概率为1/N
,而在 任何 一轮中被选中的总概率为M/N
。 - SneftelA
有1/N
的机会成为第一位,对于第二位,它也有1/(N-1)
*(N-1)/N
=1/N
的机会,因为(N-1)/N
是它不被选为第一位的概率。以此类推,A
有M/N
的机会成为任何一个选择。正确吗? - Jackson Tale