Algorithm 不属于集合的等概率样本数

Algorithm 不属于集合的等概率样本数,algorithm,probability,Algorithm,Probability,我有一个数字n和一组数字S∈ [1..n]*大小s(大大小于n)。我想取样一个数字k∈ [1..n]概率相等,但不允许该数字出现在集合S中 我正在尝试用最坏的O(logn+s)解决这个问题。我不确定这是否可能 一种简单的方法是创建一个从1到n的数字数组,排除S中的所有数字,然后选择一个数组元素。这将在O(n)中运行,不是一个选项 另一种方法可能只是生成随机数∈[1..n]并拒绝包含在S中的内容。这没有理论界限,因为任何数字即使在集合中也可以多次采样。但是,如果s比n小得多,那么平均而言,这可能是

我有一个数字
n
和一组数字
S∈ [1..n]*
大小
s
(大大小于
n
)。我想取样一个数字
k∈ [1..n]
概率相等,但不允许该数字出现在集合
S

我正在尝试用最坏的
O(logn+s)
解决这个问题。我不确定这是否可能

一种简单的方法是创建一个从1到n的数字数组,排除
S
中的所有数字,然后选择一个数组元素。这将在
O(n)
中运行,不是一个选项


另一种方法可能只是生成随机数
∈[1..n]
并拒绝包含在
S
中的内容。这没有理论界限,因为任何数字即使在集合中也可以多次采样。但是,如果
s
n
小得多,那么平均而言,这可能是一个实用的解决方案。生成一个介于1和n-s之间的随机数,称之为k。我们选择了{1,…,n}-s的第k个元素。现在我们需要找到它


在s上使用二进制搜索查找s元素的计数实际上,拒绝方法似乎是一种实用的方法。 在
1…n
中生成一个数字,检查是否禁止;重新生成,直到生成的编号不被禁止

单个拒绝的概率为
p=s/n
。 因此,预期的随机数生成数是
1+p+p^2+p^3+…
1/(1-p)
,这反过来等于
n/(n-s)

现在,如果
s
远小于
n
,或者甚至大于
s=n/2
,则该预期数字最多为
2
。 要使它在实践中不可行,需要
s
几乎等于
n

如果使用树集合检查数字是否在集合中,则将预期时间乘以
log s
,如果是哈希集合,则仅乘以
1
(再次乘以预期值)。因此,平均时间是
O(1)
O(logs)
,具体取决于设置的实现。还有存储集合的
O(s)
内存,但除非集合以某种特殊的方式隐式和简洁地给出,否则我看不出如何避免它

(编辑:根据注释,对于给定集,您只需执行一次。 此外,如果我们运气不好,并且集合是以普通数组或列表的形式给出的,而不是一些更奇特的数据结构,那么使用这种方法我们可以得到
O(s)
预期时间,这仍然符合
O(log n+s)
的要求。)

如果对无界算法的攻击是一个问题(并且只有在它们确实存在的情况下),那么该方法可以包括一个回退算法,用于某些固定次数的迭代无法提供答案的情况。 与快速排序类似,但如果递归深度太高(这几乎肯定是导致二次快速排序行为的攻击的结果),则返回HeapSort

  • 查找禁止集合中小于或等于n-s的所有数字。称之为数组A
  • 查找不在禁止集合中且大于
    n-s
    的所有数字。称之为数组B。如果集合已排序,则可以在O(s)中完成
  • 请注意,A和B的长度相等,并创建映射
    map[A[i]]=B[i]
  • 生成编号
    t
    n-s
    。如果有
    map[t]
    返回它,否则返回
    t

  • 它将在
    O(s)
    插入到map+1查找中工作,这是一个平均值为O(s)或
    O(s日志s)
    这是一个O(1)解决方案,带有O(s)初始设置,通过将每个不允许的数字>s映射到允许的数字很好的问题-我同意,我认为这是不可能的(尽管我很想看到一个解决方案)。迭代方法是一个很好的折衷方案,因为在随机数出现在集合中的概率变得非常小(即使
    s
    n
    小不了多少)之前,你不需要多次迭代来选取随机数。随机数非常便宜,因为您不需要加密强度高的数或任何东西,而且您的查找是
    O(1)
    ,因此它应该是高效的。您可以随时使用
    O(n)进行“回退”
    如果您生成的数字超过100个,请选择该方法。您对单个数字的成本感兴趣还是对多个数字的平均成本感兴趣?例如,生成一个可重复使用的数据结构的成本是多少?他说,最坏的情况是OP说他想要一个比O(log N+S)更好的解决方案,并不是说他对最坏情况的性能感兴趣。显然,他不仅仅对最坏情况的性能感兴趣,否则将不考虑随机拉斯维加斯算法,因为它的最坏情况是无界的。@TrentP我对最坏情况感兴趣。只有在我找不到更好的解决方案时才会考虑随机算法,我对此不满意不幸的是,我也不能分摊成本,因为我只需要为每一组选择一个样本(但对其他组经常重复)。这是一个很好的方法(而且
    O(s)
    ,即使没有为清晰起见编辑的假设
    |s |@hnefatl。我提到sdef substitute(S):
        H = dict()
        j = 1
        for s in S:
            if s > len(S):
                while j in S: j += 1
                H[s] = j
                j += 1
        return H
    
    def myrand(n, s, H):
        k = random.randint(s + 1, n)
        return (H[k] if k in H else k)
    
    def rand_not_in(n, S):
        k = random.randint(len(S) + 1, n);
        if k not in S: return k
        j = 1
        for s in S:
            if s > len(S):
                while j in S: j += 1
                if s == k: return j
                j += 1