具有相等概率的样本编号,不是集合的一部分

时间:2018-05-26 20:26:09

标签: algorithm probability

我有一个n号码和一组大小为S ∈ [1..n]*的{​​{1}}号码(大大小于s)。我想以相同的概率对n数字进行抽样,但不允许该数字位于集k ∈ [1..n]中。

我试图在最坏的S解决问题。我不确定是否可能。

一种天真的方法是创建一个从1到n的数字数组,不包括O(log n + s)中的所有数字,然后选择一个数组元素。这将在S中运行,不是一种选择。

另一种方法可能只是生成随机数O(n),如果它们包含在∈[1..n]中,则拒绝它们。这没有理论上的约束,因为任何数字都可以被多次采样,即使它在集合中也是如此。但是,如果S远小于s,这可能是一个实际的解决方案。

4 个答案:

答案 0 :(得分:5)

说s已排序。生成1到n-s之间的随机数,称之为k。我们选择了{1,...,n} - s的第k个元素。现在我们需要找到它。

在s上使用二进制搜索来查找s< = k的元素计数。这需要O(log | s |)。将此添加到k。在这样做时,我们可能已经通过或者达到了s的其他元素。我们可以通过递增我们传递的每个这样的元素的答案来调整这个,我们通过从二进制搜索中找到的点检查s的下一个更大的元素来找到它。

例如,n = 100,s = {1,4,5,22},我们的随机数是3.所以我们的方法应该返回[2,3,6,7,...的第三个元素, 21,二元搜索发现1个元素最多为3,所以我们增加到4.现在我们比较下一个更大的元素,它是4,所以增量到5重复这个找到5,所以我们增加到6.我们再次检查s,看到6不在其中,所以我们停止。

例如,n = 100,s = {1,4,5,22},我们的随机数是4.所以我们的方法应该返回[2,3,6,7,...的第四个元素,二元搜索发现2个元素最多为4个,所以我们增加到6个。现在我们比较下一个更大的s元素,它是5,所以增量到7我们再次检查s,看到下一个数字是> 7,所以我们停下来。

如果我们假设“s基本上小于n”意味着| s | < = log(n),那么我们最多会增加log(n)次,最多只增加s次。

如果s未排序,那么我们可以执行以下操作。创建一个大小为s的位数组。生成k。解析并做两件事:1)计算元素的数量< k,叫这个r。同时,如果k + i在s中,则将第i位设置为1(0如果索引,则如果k在s中则设置第一位)。

现在,增加k的次数等于r加上设定位数是指数< =增加次数的数组。

例如,n = 100,s = {1,4,5,22},我们的随机数是4.所以我们的方法应该返回[2,3,6,7,...的第四个元素, 21,23,24,...,100]这是7.我们解析s和1)注意1个元素低于4(r = 1),2)将我们的数组设置为[1,1,0,0 ]。我们为r = 1递增一次,对于两个设置位再递增两次,最后为7。

这是O(s)时间,O(s)空间。

答案 1 :(得分:4)

这是具有O(s)初始设置的O(1)解决方案,其通过映射每个不允许的数字>来工作。 s到允许的数字< = s。

S成为一组不允许的值S(i),其中i = [1 .. s]s = |S|

这是一个两部分算法。第一部分仅在S时间基于O(s)构建哈希表,第二部分在k ∈ {1..n}, k ∉ S时间内找到随机值O(1),假设我们可以生成均匀随机在恒定时间内连续范围内的数字。哈希表可以重新用于新的随机值,也可以用于新的n(当然假设S ⊂ { 1 .. n }仍然存在)。

构造哈希H。首先设置j = 1。然后迭代S(i) S的元素。它们不需要排序。如果S(i) > s,请将键值对(S(i), j)添加到哈希表中,除非j ∈ S,在这种情况下增加j,直到哈希表为止。最后,增加j

要查找随机值k,请首先生成s + 1n范围内的均匀随机值。如果kH中的关键字,那么k = H(k)。即,我们最多只进行一次哈希查询,以确保k不在S

用于生成哈希的Python代码:

def 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

对于实际实现为O(s),可能需要将S转换为类似frozenset的内容,以确保成员资格的测试为O(1)并移动{{} 1}}循环不变循环。假设len(S)测试和插入哈希(j in S)是恒定时间,这应该具有复杂度O(s)。

随机值的生成就是:

H[s] = j

如果只对每个def myrand(n, s, H): k = random.randint(s + 1, n) return (H[k] if k in H else k) 的单个随机值感兴趣,则可以优化算法以改善常见情况,而最坏情况保持不变。这仍然需要S在哈希表中,该哈希表允许恒定时间“元素”测试。

S

优化:仅当随机值在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 中时才生成映射。不要将映射保存到哈希表。当找到随机值时,使映射生成短路。

答案 2 :(得分:3)

实际上,拒绝方法似乎是实用的方法。 在Component中生成一个数字并检查它是否被禁止;重新生成,直到不禁止生成的数字。

单次拒绝的概率为1...n。 因此,预期的随机数生成数为p = s/n which is 1 + p + p^2 + p^3 + ...,后者又等于1/(1-p)

现在,如果n/(n-s)远远小于s,或者甚至更多n,则此预期数字最多为s = n/22几乎等于s使其在实践中变得不可行。

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

编辑:根据评论,对于给定的集合,您只执行一次此操作。 另外,如果我们运气不好,并且该集合作为普通数组或列表给出,而不是一些更高级的数据结构,我们通过这种方法得到O(s)预期时间,这仍然适合{{1}要求。)

如果针对无界算法的攻击是一个问题(并且只有它们确实存在),该方法可以包括针对当某些固定数量的迭代没有提供答案的情况的回退算法。 类似于IntroSort是QuickSort,但如果递归深度过高(这几乎肯定是攻击导致二次QuickSort行为的结果),则回退到HeapSort。

答案 3 :(得分:2)

  1. 查找禁用集中的所有数字,然后小于或等于n-s。称之为数组A.
  2. 查找不在禁用集中且大于n-s的所有数字。将其称为数组B.如果已对其进行排序,则可以在O(s)中完成。
  3. 请注意,A和B的长度相等,并创建映射map[A[i]] = B[i]
  4. 生成号码t,最高为n-s。如果map[t]返回,则返回t
  5. 它可以O(s)插入地图+ 1查找,可以是平均为O(s)或O(s log s)