python,从(1,n)中随机选择#k数,不包括列表中的数字

时间:2017-11-03 05:25:41

标签: python algorithm

对于给定的exclude_list = [3,5,8],n = 30,k = 5

我想在1到30之间选择5(k)个随机数。 但我不应该在exclude_list中选择数字

假设exclude_list,n可能很大。

如果不需要排除,很容易获得k个随机样本

rand_numbers = sample(range(1, n), k)

所以为了得到答案,我可以做到

sample(set(range(1, n)) - set(exclude_numbers), k)

我读到该范围一次在内存中保留一个数字。 我不太确定它如何影响上面两行。

第一个问题是,下面的代码是否将所有n个数字放入内存中,或者是否一次放入每个数字?

rand_numbers = sample(range(1, n), k)

第二个问题是,如果上面的代码确实一次在内存中放入一个数字,我可以使用排除列表的附加约束来做类似的事情吗?

1 个答案:

答案 0 :(得分:2)

sample's docstring中的示例说明:

  

要选择整数范围内的样本,请使用range作为参数。   这对于从a采样来说尤其快速且节省空间   人口众多:样本(范围(10000000),60)

我可以在我的机器上测试:

In [11]: sample(range(100000000), 3)
Out[11]: [70147105, 27647494, 41615897]

In [12]: list(range(100000000))  # crash/takes a long time

有效地使用排除列表进行采样的一种方法是使用相同的范围技巧,但“跳过”排除项(我们可以在O(k * log(len(exclude_list)))中使用{{3 }}:

import bisect
import random

def sample_excluding(n, k, excluding):
    # if we assume excluding is unique and sorted we can avoid the set usage...
    skips = [j - i for i, j in enumerate(sorted(set(excluding)))]
    s = random.sample(range(n - len(skips)), k)
    return [i + bisect.bisect_right(skips, i) for i in s]

我们可以看到它有效:

In [21]: sample_excluding(10, 3, [2, 4, 7])
Out[21]: [6, 3, 9]

In [22]: sample_excluding(10, 3, [1, 2, 8])
Out[22]: [0, 4, 3]

In [23]: sample_excluding(10, 6, [1, 2, 8])
Out[23]: [0, 7, 9, 6, 3, 5]

具体而言,我们在不使用O(n)内存的情况下完成了这项工作:

In [24]: sample_excluding(10000000, 6, [1, 2, 8])
Out[24]: [1495143, 270716, 9490477, 2570599, 8450517, 8283229]
相关问题