我的目标是从0,... n-1采样k个整数而不重复。采样整数的顺序无关紧要。在每次呼叫(经常发生)时,n和k将稍微变化但不多(n约为250,000,k约为2,000)。我想出了以下摊销的O(k)算法:
我认为排列/组合的统一抽样应该是一个详尽研究的问题,所以(1)有一个更好的解决方案,或者至少(2)我的解决方案是一个众所周知的解决方案(一个小修改) 。因此,
请帮帮我。感谢。
答案 0 :(得分:1)
如果k
远小于n
- 比如说,不到n
的一半 - 那么最有效的解决方案是保持哈希中生成的数字table(实际上是一个哈希集,因为没有与键关联的值)。如果随机数恰好已经在哈希表中,则拒绝它并在其位置生成另一个。根据建议的k
和n
的实际值(k ∼ 2000; n ∼ 250,000
),生成k
个唯一样本的预期拒绝次数小于10,因此几乎不会引起注意。哈希表的大小为O(k),可以在样本生成结束时删除它。
还可以使用哈希表而不是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
,它也有工作的优势。
最后,在您提出的算法中,实际上很容易在O(k)时间内恢复A
。只有在以下情况下,算法才会修改值A[j]
:
一个。 n − k ≤ j < n
或
湾有一些i
,n − k ≤ i < n
和A[i] = j
。
因此,您可以通过查看A
的每个A[i]
来恢复向量n − k ≤ i < n
:首先,如果A[i] < n−k
,请将A[A[i]]
设置为{{1} }};然后,无条件地将A[i]
设置为A[i]
。