使用Reservoir Sampling一次性解决此问题
如果您想从事先不知道该列表长度的项目列表中随机选择一个或多个项目,您可以使用Reservoir Sampling。
我们可以利用这一点,连同File.ReadLines()
方法(避免缓冲内存中的所有行)编写一个单遍算法,该算法将只读取每行一次,而不进行缓冲。
下面的示例代码显示了一个通用解决方案,可让您随机选择任意数量的行。对于您的情况,N = 1。
示例代码还包括一个测试程序,以证明这些行是随机选择的,分布均匀。
(要了解此代码的工作原理,请参阅我上面链接的 Wiki 文章。)
using System;
using System.IO;
using System.Collections.Generic;
namespace Demo
{
internal class Program
{
public static List<string> RandomlyChooseLinesFromFile(string filename, int n, Random rng)
{
var result = new List<string>(n);
int index = 0;
foreach (var line in File.ReadLines(filename))
{
if (index < n)
{
result.Add(line);
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = line;
}
++index;
}
return result;
}
// Test RandomlyChooseLinesFromFile()
private static void Main(string[] args)
{
Directory.CreateDirectory("C:\\TEST");
string testfile = "C:\\TEST\\TESTFILE.TXT";
File.WriteAllText(testfile, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9");
var rng = new Random();
int trials = 100000;
var counts = new int[10];
for (int i = 0; i < trials; ++i)
{
string line = RandomlyChooseLinesFromFile(testfile, 1, rng)[0];
int index = int.Parse(line);
++counts[index];
}
// If this algorithm is correct, each line should be chosen
// approximately 10% of the times.
Console.WriteLine("% times each line was chosen:\n");
for (int i = 0; i < 10; ++i)
{
Console.WriteLine("{0} = {1}%", i, 100*counts[i]/(double)trials);
}
}
}
}