遍历从0到N的所有数字m,决定是否将m包含在遇到的集合中。您需要根据已处理的数字更新包含下一个数字的概率。
让我们将这个想法应用到给出的示例中,n=3 和 N=5。首先考虑m=0。剩下 3 个数字和 5 种可能性,所以 0 在集合中的概率为 3/5。使用随机数生成器来决定是否包含该数字。现在考虑 m=1。如果您在集合中包含 0,那么您还有 2 个数字和 4 个可能性,因此应该以 2/4 的概率包含它,但如果不包含 0,那么您还有 3 个数字和 4 个可能性,因此应该包含 1概率为 3/4。这将一直持续到所需的 3 个数字包含在集合中。
这是 Python 中的一个实现:
from __future__ import division
import random
def rand_set(n, N):
nums_included=set()
for m in range(N):
prob = (n-len(nums_included)) / (N-m)
if random.random() < prob:
nums_included.add(m)
return nums_included
您可以(并且可能应该)添加一个测试,以查看您的集合中何时有足够的数字,并尽早跳出循环。
数字存储在一个集合中,大小从 0 到 n 不等,所以使用的存储是O(n)。其他所有内容都使用恒定空间,因此总体上是O(n)。
编辑实际上,您可以使用这种方法走得更远一些,这样它就会占用恒定的空间。在 Python 中,只需根据上述内容制作一个生成器即可:
def rand_set_iter(n, N):
num_remaining = n
m = 0
while num_remaining > 0:
prob = num_remaining / (N-m)
if random.random() < prob:
num_remaining -= 1
yield m
m += 1
在这里,我继续使用 while 循环而不是 for 循环。要存储结果,您当然需要使用O(n) 空间。但是,如果您需要做的只是遍历数字,生成器版本会在 O(1) 中完成。
对于没有生成器的语言,您可以滚动自己的生成器,重复调用函数并更新静态或全局变量。