【问题标题】:Fast random selection algorithm快速随机选择算法
【发布时间】:2009-12-03 15:26:20
【问题描述】:

给定一组真/假值,随机选择具有真值的索引的最有效算法是什么。

一个简单的算法是

a <- the array
c <- 0
for i in a:
    if a[i] is true: c++
e <- random number in (0, c-1)
j <- 0
for i in e:
    while j is false: j++
return j

谁能想出一个更快的算法?也许有一种方法可以只遍历列表一次,即使一开始不知道真实元素的数量?

【问题讨论】:

  • 只是想知道,这些类型的算法在哪些应用程序中使用?前段时间,我遇到了一个类似的问题,给定一个无限大小的数组,前 n 个位置用 1 填充,其余的都是零。现在这个数组被提供给一个新用户(他不知道 n 的值)。现在找出一种算法来标记最后 1 所在的位置。我通过二进制搜索解决了这个问题。请举一些使用这些的例子。
  • 近乎重复:stackoverflow.com/questions/1133942/…。但是,在那个问题中,数组的大小为 52,这可能会影响答案(例如,您很确定大小为 52 的数组适合内存,而这里的 a 可能不适合)。

标签: algorithm random


【解决方案1】:

使用“从无限列表中选择随机元素”算法。

保留您当前选择的索引,并计算您看到的真实值的数量。

当您看到一个真实值时,增加计数,然后将您的选择替换为当前索引,概率为 P=(1/count)。 (所以你总是选择你找到的第一个......然后你可能切换到第二个,概率为 1/2,然后你可能 em> 以 1/3 等的概率切换到第三个)

这只需要对列表进行一次扫描并持续存储。 (但是,它确实需要您计算出更多的随机数。)特别是,它不需要您缓冲列表或返回开始 - 因此它可以在无限制的输入流上工作。

请参阅this answer 以获得简单的“选择随机元素”算法的示例 LINQ 实现;它只需要稍作调整。

【讨论】:

  • 更多细节和证明在这里:stackoverflow.com/questions/1133942/…。这个问题在功能上是那个问题的重复,尽管措辞有点不同。我的直觉是,假设数据在内存中,它很可能会比两遍算法慢。但值得测试,如果两次通过的性能由于某种原因不可接受。
  • @Steve:这取决于“真实”值的稀疏性与生成随机数的成本。如果列表中有 100 万个条目,其中只有 2 个是“真实的”,那么这很可能是一场胜利。另一方面,如果您有一百万个全部为真的条目,那么两遍算法可能会更快。总的来说,我只是喜欢一次性常量存储算法的优雅:)
  • 嘿,我刚刚对 Johannes 的回答发表了关于稀疏性的相同评论。我也同意优雅的观点,尽管我有点担心使用大量随机数会使分析 RNG 中任何弱点的影响变得更加困难。
  • 感谢 Jon 和 Steve,我怀疑有这样的技术,但我以前不知道!
【解决方案2】:

使用指向true 值的索引构建一个列表,然后随机选择其中一个。列表遍历需要 O(n),随机数需要尝试一次。

【讨论】:

  • 这肯定比我想出的要快,尽管它使用 O(n) 工作空间,而我只使用恒定的工作空间。所以可能还有改进的空间。
  • 肯定更快吗?如果真值非常罕见,那么它几乎肯定会更快。如果错误值非常罕见,那么它几乎肯定会更慢。收支平衡点在哪里,我不知道。
  • 是的,真/假值的分布对于哪种算法更有效的问题确实很重要。但是,当这不为人知时,所有的赌注都会像往常一样被取消。不过,我发现 Jon 的回答非常好,而且可能比这更好。
  • @Steve:这真的取决于你需要多久做一次。建立索引只发生一次,因此即使错误值极少,使用索引最终也会更快。
  • 是的,我没有想到:可能会保证在下次更改数组内容之前完成一定数量的选择。如果允许我们稍微扩大范围,那么最好完全摆脱标志数组,只保留一个包含“真实”索引的结构。插入/移除会更慢,但选择会更快。
猜你喜欢
  • 1970-01-01
  • 2016-04-27
  • 2011-07-14
  • 1970-01-01
  • 2010-10-13
  • 2014-10-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多