从文件中随机选择行


问题内容

我有一堆文件,而且文件头有5行。在文件的其余部分,双行构成一个条目。我需要从这些文件中随机选择条目。如何选择随机文件和随机条目(每行,不包括标题)?


问题答案:

如果文件足够小,则将成对的行读取到内存中,然后从该数据结构中随机选择。如果文件太大,则Eugene
Y提供正确的答案:使用储层采样

这是该算法的直观解释。

Process the file line by line.
pick = line, with probability 1/N, where N = line number

换句话说,在第1行,我们将以1/1概率选择第1行。在第2行,我们将选择权 更改
为第2行1/2。在第3行,我们将按1/3概率将选择权更改为第3行。等等。

为了直观地证明,想象一下一个包含3行的文件:

        1            Pick line 1.
       / \
     .5  .5
     /     \
    2       1        Switch to line 2?
   / \     / \
 .67 .33 .33 .67
 /     \ /     \
2       3       1    Switch to line 3?

每个结果的概率:

Line 1: .5 * .67     = 1/3
Line 2: .5 * .67     = 1/3
Line 3: .5 * .33 * 2 = 1/3

从那里开始,剩下的就是归纳法。例如,假设文件有4行。我们已经说服自己,从第3行开始,到目前为止的每一行(1、2、3)都有相等的机会成为我们 当前的
选择。当我们前进到第4行时,将有1/4机会被选中-正好是应该的位置,从而将前3行的概率降低为正确的数量(1/3 * 3/4 = 1/4)。

这是Perl常见问题解答,适用于您的问题。

use strict;
use warnings;

# Ignore 5 lines.
<> for 1 .. 5;

# Use reservoir sampling to select pairs from remaining lines.
my (@picks, $n);
until (eof){
    my @lines;
    $lines[$_] = <> for 0 .. 1;

    $n ++;
    @picks = @lines if rand($n) < 1;
}

print @picks;