来自[0:n)的k个整数的均匀采样

时间:2017-08-18 17:28:25

标签: algorithm random uniform-distribution

我的目标是从0,... n-1采样k个整数而不重复。采样整数的顺序无关紧要。在每次呼叫(经常发生)时,n和k将稍微变化但不多(n约为250,000,k约为2,000)。我想出了以下摊销的O(k)算法:

  1. 使用项目0,1,2,...,n-1准备阵列A.这需要O(n),但由于n相对稳定,因此可以使成本摊销不变。
  2. 从[0:i]中抽取随机数r,其中i = n - 1.这里的成本实际上与n有关,但由于n不是很大,这种依赖性并不重要。
  3. 交换第r项和第A列中的第i项。
  4. 将我减少1。
  5. 重复步骤2~4的k次;现在我们在A的尾部有一个长度为k的随机排列。复制这个。
  6. 我们应该将A回滚到其初始状态(0,...,n-1)以保持步骤1的成本不变。这可以通过在步骤2的每次通过时将r推到长度为k的堆栈来完成。堆栈的准备需要摊销不变的成本。
  7. 我认为排列/组合的统一抽样应该是一个详尽研究的问题,所以(1)有一个更好的解决方案,或者至少(2)我的解决方案是一个众所周知的解决方案(一个小修改) 。因此,

    • 如果是(1),我想知道更好的解决方案。
    • 如果是(2),我想找一个参考。

    请帮帮我。感谢。

1 个答案:

答案 0 :(得分:1)

  1. 如果k远小于n - 比如说,不到n的一半 - 那么最有效的解决方案是保持哈希中生成的数字table(实际上是一个哈希集,因为没有与键关联的值)。如果随机数恰好已经在哈希表中,则拒绝它并在其位置生成另一个。根据建议的kn的实际值(k ∼ 2000; n ∼ 250,000),生成k个唯一样本的预期拒绝次数小于10,因此几乎不会引起注意。哈希表的大小为O(k),可以在样本生成结束时删除它。

  2. 还可以使用哈希表而不是n值的向量来模拟FYK shuffle算法,从而避免必须拒绝生成的随机数。如果您使用的是向量A,则应首先将A[i]初始化为i,每0 ≤ i < k。使用哈希表H,您可以从空哈希表开始,如果密钥H[i]不在哈希中,则使用i被视为i的约定表。算法中的第3步 - &#34;与A[r]交换A[i]&#34; - 变为&#34;添加H[r]作为样本的下一个元素,并将H[r]设置为H[i]&#34;。请注意,无需设置H[i],因为该元素永远不会再次引用:所有后续随机数r都是从不包含i的范围生成的。

    因为在这种情况下哈希表包含键和值,它大于上面替代1中使用的哈希集,并且增加的大小(以及随之而来的内存缓存未命中的增加)可能导致比通过消除拒绝来节省。但是,即使k偶尔接近n,它也有工作的优势。

  3. 最后,在您提出的算法中,实际上很容易在O(k)时间内恢复A。只有在以下情况下,算法才会修改值A[j]

    一个。 n − k ≤ j < n

    湾有一些in − k ≤ i < nA[i] = j

    因此,您可以通过查看A的每个A[i]来恢复向量n − k ≤ i < n:首先,如果A[i] < n−k,请将A[A[i]]设置为{{1} }};然后,无条件地将A[i]设置为A[i]