В этом примере алгоритм реализован не очень хорошо и понятно ... Некоторый псевдокод, который лучше объясняет его, будет:
cnt = 0
while not end of file {
read line
cnt = cnt + 1
if random(1 to cnt) = 1 {
result = line
}
}
Как видите, идея в том, что вы читаете каждую строку в файле и вычисляете вероятность того, что строка должна быть выбранной. После прочтения первой строки вероятность составляет 100%, после прочтения второй строки вероятность составляет 50% и т. Д.
Это можно расширить, выбрав N элементов, сохранив массив с размером N вместо одной переменной и рассчитав вероятность замены строки одним из текущих в массиве:
var result[1..N]
cnt = 0
while not end of file {
read line
cnt = cnt + 1
if cnt <= N {
result[cnt] = line
} else if random(1 to cnt) <= N {
result[random(1 to N)] = line
}
}
Edit:
Вот код, реализованный в C #:
public static List<string> GetRandomLines(string path, int count) {
List<string> result = new List<string>();
Random rnd = new Random();
int cnt = 0;
string line;
using (StreamReader reader = new StreamReader(path)) {
while ((line = reader.ReadLine()) != null) {
cnt++;
int pos = rnd.Next(cnt);
if (cnt <= count) {
result.Insert(pos, line);
} else {
if (pos < count) {
result[pos] = line;
}
}
}
}
return result;
}
Я сделал тест, выполнив метод 100000 раз, выбрав 5 строк из 20 и посчитал вхождения строк. Это результат:
25105
24966
24808
24966
25279
24824
25068
24901
25145
24895
25087
25272
24971
24775
25024
25180
25027
25000
24900
24807
Как видите, дистрибутив настолько хорош, насколько вы могли когда-либо захотеть. :)
(Я переместил создание объекта Random
из метода при запуске теста, чтобы избежать проблем с заполнением, поскольку начальное число берется из системных часов.)
Примечание:
Вы можете захотеть зашифровать порядок в результирующем массиве, если хотите, чтобы они были упорядочены случайным образом. Поскольку первые N строк располагаются в массиве по порядку, они не располагаются случайным образом, если они остаются в конце. Например, если N равно трем или больше и выбрана третья строка, она всегда будет на третьей позиции в массиве.
Редактировать 2:
Я изменил код, чтобы использовать List<string>
вместо string[]
. Это позволяет легко вставлять первые N элементов в случайном порядке. Я обновил тестовые данные из нового тестового прогона, чтобы вы могли видеть, что распределение все еще в порядке.