Марк отвечает почти правильно, за исключением двух вопросов:
- Если строка длиннее
length - 1
символов (включая новую строку), то цикл while
будет увеличивать count
не менее чем в два раза для одной и той же строки: один раз для первых length - 1
символов, другой для следующие length - 1
символов и т. д.
- Вычисление
rand() * count
может вызвать переполнение целых чисел.
Чтобы решить первую проблему, вы можете вызвать fgets
в буфер для мусора до тех пор, пока он не вернет NULL
(что указывает на ошибку ввода-вывода или EOF без чтения данных) или в буфере для мусора есть новая строка:
count = 0;
while (fgets(line, length, stream) != NULL)
{
char *p = strchr(line, '\n');
if (p != NULL) {
assert(*p == '\n');
*p = '\0'; // trim the newline
}
else { // haven't reached EOL yet. Read & discard the rest of the line.
#define TRASH_LENGTH 1024
char trash[TRASH_LENGTH];
while((p = fgets(trash, TRASH_LENGTH, stream)) != NULL) {
if ((p = strchr(trash, '\n')) != NULL) // reached EOL
break;
}
}
assert(strchr(line, '\n') == NULL); // `line` does not contain a newline
count++;
// ...
Вторую проблему можно решить с помощью предложения @ tvanfosson, если арифметика с плавающей точкой недоступна:
int one_chance_in(size_t n)
{
if (rand() % n == 0) // `rand` returns an integer in [0, `RAND_MAX`]
return 1;
else
return 0;
}
Но обратите внимание, что rand() % n
не является равномерной, дискретной случайной величиной , даже если rand()
предполагается равной единице, потому что вероятность того, что rand() % n == 0
может составлять 1 / RAND_MAX
выше, чем желаемая вероятность 1 / n
. На моей машине RAND_MAX
равно 2147483647, поэтому разница составляет 4,66 × 10 -10 , но стандарт C требует только, чтобы RAND_MAX
было не менее 32767 (3,05 × 10 -5 *). 1035 * разница).
Кроме того, для всех, кто интересуется, почему эта схема работает (как я), было бы полезно проработать расчет вероятности того, что первая строка останется в keptline
, если есть m линии и обобщение: в первой итерации цикла вероятность того, что первая строка будет скопирована в keptline
, равна 1/1. Во второй итерации цикла вероятность того, что вторая строка будет не перезаписать первую строку, равна 1/2. На третьей итерации вероятность того, что третья строка будет не перезаписать первую строку, равна 2/3. Продолжая, вероятность того, что последняя строка не перезапишет первую строку, равна ( m - 1) / m . Таким образом, вероятность того, что первая строка останется в keptline
после итерации по всем строкам:
1/1 × 1/2 × 2/3 × 3/4 × ... × ( м - 2) / ( м - 1) × ( м - 1) / м = 1 / м
Вероятность того, что вторая строка останется в keptline
, равна:
1/2 × 2/3 × 3/4 × ... × ( м - 2) / ( м - 1) × ( м - 1) / м = 1 / м
Вероятность того, что третья строка останется в keptline
, равна:
1/3 × 3/4 × ... × ( м - 2) / ( м - 1) × ( м - 1) / м = 1 / м
Etc. Все они 1 / м .