如何在不事先读取整个文件的情况下对文件中的行进行随机播放?

时间:2010-07-29 23:34:55

标签: algorithm file random shuffle

在不事先读取整个文件的情况下,对文件中的行进行随机播放有什么好的算法?

我想它看起来像这样:从头开始逐行读取文件,在每个点存储行,并决定是否要打印到目前为止存储的一行(然后从存储中删除)或什么也不做,继续下一行。

有人可以验证/证明这个和/或可能发布工作(perl,python等)代码吗?

相关问题,但没有考虑节省内存的算法:

3 个答案:

答案 0 :(得分:4)

我想不出一种方法来随机地执行整个文件而不以某种方式维护已经写入的内容的列表。我想如果我必须做一个内存有效的shuffle,我会扫描文件,为新行建立一个偏移列表。一旦我有了这个新线偏移列表,我会随机选择其中一个,将其写入stdout,然后将其从偏移列表中删除。

我不熟悉perl或python,但可以用php演示。

<?php
$offsets = array();

$f = fopen("file.txt", "r");
$offsets[] = ftell($f);
while (! feof($f))
{
  if (fgetc($f) == "\n") $offsets[] = ftell($f);
}

shuffle($offsets);
foreach ($offsets as $offset)
{
  fseek($f, $offset);
  echo fgets($f);
}
fclose($f);
?>

我能想到的唯一其他选择,如果扫描文件中的新行是绝对不可接受的,那就是(我不打算把这个编码出来):

  1. 确定文件大小
  2. 创建已写入stdout的偏移量和长度列表
  3. 循环直到bytes_written == filesize
  4. 寻找已经写入的值列表中尚未存在的随机偏移量
  5. 从该搜索备份到上一个换行符或文件开头
  6. 显示该行,并将其添加到写入的偏移和长度列表
  7. 转到3。

答案 1 :(得分:3)

以下算法在输入文件的行数中为线性

预处理:

  1. 通过扫描换行符(或其他内容)查找n(总行数),但存储表示每行开头和结尾的字符编号。因此,您有2个向量,例如se,其大小为n,其中输入文件中从s[i]e[i]的字符数为{ {1}}行。在C ++中,我使用vector

  2. 随机置换从1到i的整数向量(在C ++中它将是random_shuffle)并将其存储在一个向量中,例如n(例如{{ 1}}成为p)。这意味着新文件的行1 2 3 4现在是原始文件中的行p = [3 1 4 2](即在上面的示例中,新文件的第一行是第3行原始文件)。

  3. 主要

    1. 创建新文件

    2. 通过阅读ip[i]之间原始文件中的文字并将其附加到新文件,在新文件中写下第一行。

    3. 继续执行所有其他行的步骤2。

    4. 因此,如果您假设读/写&amp;而整体复杂度在行数中是线性的(因为s[p[0]]是线性的)。在文件中搜索(递增文件指针)都是常量时间操作。

答案 2 :(得分:0)

您可以为N个字符串创建一个数组,并将该文件的前N行读入此数组。对于其余部分,您读取一行,从数组中随机选择一行,并用新读取的字符串替换此字符串。您还要将数组中的字符串写出到输出文件中。这样做的好处是您不需要两次迭代文件。缺点是它不会创建一个非常随机的输出文件,特别是当N为低时(例如,此算法不能在输出中移动最后一行超过N行。)

修改

在python中只是一个例子:

import sys
import random

CACHE_SIZE = 23

lines = {}

for l in sys.stdin: # you can replace sys.stdin with xrange(200) to get a test output
    i = random.randint(0, CACHE_SIZE-1)
    old = lines.get(i)
    if old:
        print old,
    lines[i] = l

for ignored, p in lines.iteritems():
    print p,