从庞大的列表中进行高效的随机抽样

时间:2011-09-20 16:27:45

标签: performance random sampling random-sample

我有一个包含大量值(53,000,000+)的数据文件,我想提取这些值的 n 的随机子集(比如2,000,000)。我实现了一个Perl脚本,它将列表拉入内存,使用Fisher-Yates method来重新排列数组,然后打印出混洗列表中的第一个 n 值。然而,即使在更小的测试集(50,000个值)上,这个改组过程也需要花费很多时间。

我正在寻找一种更有效,可扩展的方法来识别大量值的随机子集并将其打印出来。有什么建议吗?

更新:根据答案和更多搜索,看起来正确的术语是“随机抽样”。

4 个答案:

答案 0 :(得分:1)

首先,检查一下shuffle的实现。如果正确实施,应该给你线性时间。此外,修改算法以在所需数量的元素被洗牌后停止:不需要(实际上和理论上)洗牌比实际输出更多的数字。

如果您要求k数字,那么这将花费您k元素操作。我怀疑你能做得更好。

答案 1 :(得分:1)

不要洗牌,这是不必要的昂贵。

在Jon Bentley的simple linear algorithm中讨论了"Programming Pearls"(Bentley说他从Knuth的"Seminumerical Algorithms"中学到了这一点)。请改用此方法。

有一些Perl implementations关于:

  

这两个片段实现了Algortihm S(3.4.2)和Algortihm R(3.4.2)   来自Knuth的编程艺术。第一个随机选择N个项目   来自元素数组,并返回对数组的引用   包含元素。请注意,它不一定会考虑   列表中的所有元素。

     

第二个从不确定大小的文件中随机选择N个项目   并返回包含所选元素的数组。记录在   文件被假定为每行,并且行被扼杀   读。这只需要通过列表1次。轻微   可以进行修改以在N的情况下使用片段   记录会超出内存限制,但这需要   稍微超过1次传递(/ msg,如果你需要这个解释)

答案 2 :(得分:1)

详细说明aix上面的答案,从项目流中选择k,一次阅读一个项目。将第一个k项保留在集S中。

现在,在阅读m项[{1}}(现在为I)时,请保持概率m>k。如果您确实保留,请从k/m随机选择一个U项,并将S替换为U

证明这会产生大小I的所有子集的概率相等,这是基于k的归纳。请注意,您无需事先知道m(项目总数),并且每个步骤n都适合。该算法是“流式” - 它不需要存储所有项目,也不需要再次传递。

答案 3 :(得分:0)

读取和改组数组会导致大量不必要的数据移动。

以下是一些想法:

一:当你说你需要一个随机子集时,你在这个上下文中究竟是什么意思“随机”?我的意思是,记录是按照任何特定顺序排列的,还是与您试图随机化的任何内容相关的顺序?

因为我的第一个想法是,如果记录不是任何相关的顺序,那么你可以通过简单地计算总大小除以样本大小来随机选择,然后选择每个第n个记录。例如,如果你有5300万条记录而你想要一个200万的样本,需要5300万/ 200万〜= 26,所以每26条记录一次。

二:如果这还不够,那么更严格的解决方案是生成200万到5300万的随机数,确保没有重复。

Two-A:如果你的样本量与记录总数相比很小,就好像你只是挑选几百或几千,我会生成一个包含多个条目的数组,并且对于每个条目,将其与所有先前条目进行比较以检查重复项。如果它是重复的,请循环并再次尝试,直到找到唯一值。

Two-B:假设您的数字不仅仅是示例而是实际值,那么您的样本数量与总人口数相比较大。在这种情况下,考虑到现代计算机上充足的内存,你应该能够通过创建一个5300万个布尔值初始化为假的布尔数组来有效地做到这一点,每个布尔当然代表一条记录。然后通过循环200万次。对于每次迭代,生成0到53百万的随机数。检查数组中相应的布尔值:如果为false,则将其设置为true。如果是,请生成另一个随机数,然后重试。

三:或者等等,这里有一个更好的想法,给出相对较大的百分比:计算你想要包含的记录的百分比。然后循环遍历所有记录的计数器。对于每个,生成从0到1的随机数,并将其与所需的百分比进行比较。如果它更少,请阅读该记录并将其包含在样本中。如果它更大,请跳过记录。

如果获取样本记录的确切数量很重要,则可以重新计算每条记录的百分比。例如 - 为了使示例简单,让我们假装你想要100个记录中的10个:

你从10/100 = .1开始所以我们生成一个随机数,说它出现了.04。 .04< .1,所以我们包括记录#1。

现在我们重新计算百分比。我们想要99个剩余的9个记录中的9个记录9 / 99~ = .0909我们的随机数是.87。这更好,所以我们跳过记录#2。

再次重新计算。我们仍然需要剩余98个记录中的9个记录。无论如何,神奇的数字是9/98。等

一旦我们获得了所需数量的记录,未来记录的概率将为零,因此我们永远不会过去。如果我们接近结束并且没有获得足够的记录,概率将非常接近100%。比如,如果我们仍然需要8条记录并且只剩下8条记录,那么概率将是8/8 = 100%,因此我们将保证接下来的记录。