Python 除指定集合中的整数外的整数样本

Python 除指定集合中的整数外的整数样本,python,random-sample,Python,Random Sample,假设我有一组整数exceptions=set([3,2,6,…])。使用python从区间[0,n)中随机均匀地抽取整数样本的num最有效的方法是什么 以下是一些我曾经尝试过的想法,但并不令人满意 # 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

假设我有一组整数
exceptions=set([3,2,6,…])
。使用
python
从区间
[0,n)
中随机均匀地抽取整数样本的
num
最有效的方法是什么

以下是一些我曾经尝试过的想法,但并不令人满意

# 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
相当大(大约10^12),因此创建集合似乎是浪费内存

然而,
len(例外)
相对较小(大约10^6),因此拒绝抽样可能是一种合理的方法

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)
samples=[]
#迭代直到我们有足够的样本
而len(样本)
不幸的是,所有的循环和集合成员测试都相当慢。显然,我可以编写
C
-扩展或使用
Cython
,但我想看看你们是否有更好的想法


编辑以在评论中包含建议。

我相信,如果异常与n相比很小,那么拒绝可能是你最好的选择;请注意,
n=10**12
e=10**6
得到无效结果的概率只有百万分之一,因此你不需要拒绝和重新随机选择大多数time。如果你需要多个样本,最好是获取更多的随机数(比如101%),然后批量过滤它们,丢弃异常(我相信numpy中有一种方法可以比循环更快地获取多个样本)。如果仍然有太多样本,只需截断结果;如果太少,生成更多样本

由于异常的规模越来越大,您可以尝试不同的方法:

假设你有一系列的
n
整数和
e
异常。事实上,你想得到一个
n-e
均匀分布的结果。所以有
x=np.random.randint(n-e)
应该足够好,只要你能快速将这样的结果转化为有意义的结果。首先,举一个例子来说明这个想法:

  • 让我们假设n=100,一组异常={11,12,13,14,15,16,17,18,19,20,26,27}
  • 案例1:您的随机结果是60。您必须计算有多少小于或等于60的异常:有13个。因此结果是60+13=73
  • 案例2:您的随机结果为22。有10个异常小于该值,因此您将结果增加10,得到32。但是,还有两个异常不大于32,但大于22,因此尚未计算。您必须将该值进一步增加2,最终结果为34
那么,算法如下所示:

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
n=…#全量程
异常=…#异常的集合
e=len(例外情况)
def get_sample():
x=np.random.randint(n-e)
跳过=0
小异常=计数小异常(x,异常)
跳过
为了实现这一点,您需要一种计算小于给定值的异常的快速方法—模块应该做到这一点,因为它在排序数组上提供了二进制搜索机制

为什么这样做?当
skipped==small_exceptions
时循环停止,因此在
0..(skipped+x)
范围内确实存在
skipped
异常。如果返回值(
skipped+x
)如果是异常,循环会更早停止,因为在上一次迭代中会出现更少的异常


准确的成本分析并不是那么容易,但是如果您假设每个
k
常规值都有一个异常,那么后续迭代中的增量每次都要小k倍。最坏的情况是
exceptions=range(e)
和random
x
显示为
0
。然后将出现
e
迭代,每一次迭代都有少量增加。如果
e
n
小,这应该不是问题;否则,您可以通过将异常存储为间隔来进一步改进此解决方案,有效地合并异常所有后续异常仅进入一个“孔”和一个操作。

什么类型的
异常
不在异常中的提案
将根据其内容具有不同的性能。例如,列表的成员资格测试为O(N)和O(1)对于集合,平均而言。make
exceptions
集合我编辑了问题,将
异常
作为
集合
,但主要问题仍然存在。