我有一个包含大量值(53,000,000+)的数据文件,我想提取这些值的 n 的随机子集(比如2,000,000)。我实现了一个Perl脚本,它将列表拉入内存,使用Fisher-Yates method来重新排列数组,然后打印出混洗列表中的第一个 n 值。然而,即使在更小的测试集(50,000个值)上,这个改组过程也需要花费很多时间。
我正在寻找一种更有效,可扩展的方法来识别大量值的随机子集并将其打印出来。有什么建议吗?
更新:根据答案和更多搜索,看起来正确的术语是“随机抽样”。
答案 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%,因此我们将保证接下来的记录。