【问题标题】:Select 100 random lines from a file with a 1 million which can`t be read into memory [closed]从无法读入内存的 100 万文件中选择 100 行随机行 [关闭]
【发布时间】:2016-06-11 08:26:51
【问题描述】:

其实这个问题是面试之一问的,不知道具体答案,能详细解释一下吗?

你如何从一个 100 万的文件中随机选择 100 行 线?无法将文件读入内存。

【问题讨论】:

  • 你的意思是来自一个有 100 万行的文件吗?
  • 是的,我就是这个意思
  • 每行的长度是恒定的吗?
  • 每行被选中的概率完全相同重要吗?
  • @GökhanAkduğan 如果行长不变,你可以选择一个数字 [0-999999] 并立即搜索到 number*line length。

标签: java algorithm file inputstream


【解决方案1】:

通常,在这种情况下,您提前不知道输入文件中的项目数量(并且您希望避免需要两次传递数据以检查可用项目的数量第一的)。在这种情况下,@radoh 和其他人提出的解决方案(您将在其中创建索引以供选择)将不起作用。

在这种情况下,您可以使用reservoir sampling:您只需要知道要选择的项目数(k 如下)并遍历输入数据(S[1..n])。以下是取自 Wikipedia 的伪代码,我将把它留给您自己练习,以将其转换为有效的 Java 方法(该方法通常类似于 List<X> sample(Stream<X> data, int k)):

/*
  S has items to sample, R will contain the result
*/
ReservoirSample(S[1..n], R[1..k])
  // fill the reservoir array
  for i = 1 to k
      R[i] := S[i]

  // replace elements with gradually decreasing probability
  for i = k+1 to n
    j := random(1, i)   // important: inclusive range
    if j <= k
        R[j] := S[i]

请注意,尽管代码明确提到了n(即输入项的数量),但您不需要在计算之前知道该值。您可以简单地遍历 IteratorStream(在您的情况下代表文件中的行),并且只需将结果数组或集合 R 保存在内存中。您甚至可以对连续流进行采样,并且在每个时间点(至少,在您看到k 样本时),您都有k 随机选择的项目。

【讨论】:

  • 我相信水库采样只会在数据中有重复项的情况下选择重复项。这与 k 接近 n 无关。
  • 通常您会多次触摸数据。例如,一旦您对它进行了水库采样,一旦您拥有(或可能拥有)计数。但它仍然是一种很好且有用的抽样技术(即使它没有使用面试问题的所有参数。)
  • 你说得对,我会解决这个问题,谢谢!
【解决方案2】:

将 100 个随机(唯一)数字(范围从 0..1000000-1)生成到一个列表中,然后通过文件从列表中读取带有索引的行。理想情况下,数字列表应该是Set

伪代码:

int i = 0;
List<String> myLines = new ArrayList();
while (fileScanner.hasNext()) {
  String line = fileScanner.nextLine();  
  if (myRandomNumbers.contains(i)) {
    myLines.add(line);
  }
  i++;
}

【讨论】:

  • @NguyenDoanTung 你能详细说明一下吗?
  • 抱歉,漏了一句话。
【解决方案3】:

这是一个非常有效的方法:

Iterator<String> linesIter = ...
List<String> selectedLines = new ArrayList();
Random rng = new Random(seed);

int linesStillNeeded = 100;
int linesRemaining = 1000000;

while (linesStillNeeded > 0) {
  String line = linesIter.next();
  linesRemaining--;
  if (rng.nextInt(linesRemaining) < linesStillNeeded) {
    selectedLines.add(line);
    linesStillNeeded--;
  }
}

我已经有一段时间没有用 Java 编写代码了,所以您可能希望将其视为伪代码。

该算法基于以下事实:任何给定的行(假设我们从总共 n 行中统一选择 k 个不同的行)将以 k/n 的概率包含在集合中。这是从

1) k 个不同行(n 行中)的数量集合是 choose(n, k),

2) 包含特定行的 k 个不同行(n 行中)的集合数是 choose(n-1, k-1),并且

3) 选择(n-1,k-1)/选择(n,k) = k/n

注意这里的k和n分别对应代码中的linesStillNeededlinesStillRemaining

【讨论】:

    猜你喜欢
    • 2020-06-17
    • 1970-01-01
    • 2021-01-16
    • 2018-11-19
    • 2016-04-29
    • 1970-01-01
    • 2019-06-19
    • 2017-12-01
    • 1970-01-01
    相关资源
    最近更新 更多