【发布时间】:2017-08-18 17:28:24
【问题描述】:
我的目标是从 0, ... n-1 中不重复地抽取 k 个整数。采样整数的顺序无关紧要。在每次调用时(经常发生),n 和 k 会略有不同,但变化不大(n 约为 250,000,k 约为 2,000)。我想出了以下摊销 O(k) 算法:
- 准备一个包含项目 0、1、2、...、n-1 的数组 A。这需要 O(n),但由于 n 相对稳定,因此可以使成本摊销为常数。
- 从 [0:i] 中采样一个随机数 r,其中 i = n - 1。这里的成本实际上与 n 相关,但由于 n 不是非常大,因此这种依赖性并不重要。
- 交换数组 A 中的第 r 项和第 i 项。
- 将 i 减 1。
- 重复k次步骤2~4;现在我们在 A 的尾部有一个长度为 k 的随机排列。复制这个。
- 我们应该将 A 回滚到其初始状态 (0, ... , n-1) 以保持步骤 1 的成本不变。这可以通过在步骤 2 的每次通过时将 r 推送到长度为 k 的堆栈来完成。堆栈的准备需要摊销的恒定成本。
我认为排列/组合的统一采样应该是一个详尽研究的问题,所以要么 (1) 有一个更好的解决方案,要么至少 (2) 我的解决方案是一个众所周知的解决方案(稍作修改) .因此,
- 在情况 (1) 中,我想知道更好的解决方案。
- 情况(2),我想找一个参考。
请帮助我。谢谢。
【问题讨论】:
-
你的方法有点像费雪-耶茨洗牌。不清楚——为什么需要回滚?
-
ad 1. 如果 n 是有界的,那么您只需要创建一次数组(前提是您最后回到订单,这是您的建议);广告 2. 我看不出它与 n 有什么关系,取一个范围内的随机数是 O(1);广告 6. 您不需要单独的堆栈,因为您在数组末尾有解决方案。
-
@Mbo 回滚是 O(k) 方法来“重新创建”一个大小为 n 的有序数组
-
@Sopel 关于 2:我不确定它是否真的需要一些成本,但我认为如果需要“真正均匀随机”采样,那么可能会有一些与 n 相关的成本。而且,您能解释一下如何使用创建的排列回滚吗?
-
@MBo 谢谢!根据维基百科,我的方法似乎被称为 Fisher-Yates shuffle 的“Durstenfeld 版本”。
标签: algorithm random uniform-distribution