假设我有一组整数exceptions = set([3, 2, 6, ...])
。除了使用num
在[0,n)
中显示的整数样本之外,从exceptions
区间python
均匀抽取# Create a set of the integers I am interested in
integers = set(range(n)) - exceptions
# Draw samples from the set
samples = np.random.choice(integers, num)
整数样本的最有效方法是什么。
以下是我曾经玩过但不满意的一些想法。
n
在我的情况下,len(exceptions)
相当大(大约10 ^ 12),因此创建集合似乎浪费了内存。
然而,samples = []
# Iterate until we have enough samples
while len(samples) < num:
# Draw a sample
proposal = np.random.randint(n)
# Accept or reject
if proposal not in exceptions:
samples.append(proposal)
相对较小(大约10 ^ 6),因此拒绝抽样可能是一种合理的方法。
C
不幸的是,所有循环和集合成员资格测试都相当缓慢。显然,我可以写一个Cython
- 扩展或使用guidata(object_handle,data)
,但我想知道你们是否有更好的主意。
已编辑以在评论中包含建议。
答案 0 :(得分:0)
我认为,如果例外情况与n相比较小,拒绝可能是您的最佳选择;请注意,使用n=10**12
和e=10**6
获得无效结果的概率只有百万分之一,所以在大多数时间没有拒绝和重新随机的情况下你会很好。如果你还需要多个样本,那么获取大量随机数(如101%)然后将它们全部过滤掉,放弃异常可能是一个好主意(我相信有一种方法可以在循环中更快地获得多个样本) )。如果还有太多,只需截断结果;如果太少,生成更多。
随着异常的大小越来越大,您可能会尝试不同的方法:
假设您有一系列n
个整数和e
个例外。事实上,您有兴趣获得n-e
均匀分布的结果之一。因此,如果您可以将这样的结果快速转换为有意义的结果,那么x = np.random.randint(n-e)
应该足够好。首先,举例说明这个想法:
然后,算法如下:
n = ... # full range
exceptions = ... # collection of your exceptions
e = len(exceptions)
def get_sample():
x = np.random.randint(n-e)
skipped = 0
small_exceptions = count_small_exceptions(x, exceptions)
while skipped < small_exceptions:
skipped = small_exceptions
small_exceptions = count_small_exceptions(x+skipped, exceptions)
return x+skipped
为了实现这一点,您需要一种快速计算小于给定值的异常的方法 - 模块bisect应该这样做,因为它在排序的数组上提供二进制搜索机制。
为什么这样做?循环在skipped == small_exceptions
时停止,因此skipped
范围内存在0..(skipped+x)
个异常。如果返回的值(skipped+x
)是异常,则循环将在之前停止,因为在上一次迭代中会有更少的异常。
确切的成本分析并不容易,但如果您假设每个k
常规值都有一个例外,那么后续迭代中的增加每次都会小k倍。最坏情况是exceptions=range(e)
和随机x
出现为0
。然后会有e
次迭代,每次迭代都会略有增加。如果e
与n
相比较小,这应该不是问题;否则,您可以通过将异常存储为间隔来进一步改进此解决方案,有效地将所有后续异常合并到一个“洞”和一个操作中。