Если диапазон достаточно мал, у вас не должно возникнуть проблем при использовании обычного метода по модулю
int GetRandomInt(int Min, int Max)
{
return (rand()%(Max-Min+1))+Min;
}
(где Min
a Max
указывает закрытый интервал, [Min
,Max
])
и вызывать его один раз для каждого броска костейНе забудьте позвонить srand(time(NULL))
в начале приложения (в начале только , а не каждый раз, когда вы хотите получить случайное число), чтобы запустить генератор случайных чисел.
Если диапазон начинает увеличиваться, возможно, вам придется столкнуться с двумя проблемами:
Во-первых, диапазон rand()
явно не равен [0, + ∞), вместо этого он равен [0, RAND_MAX
], где RAND_MAX
- это #define
, гарантированное равное не менее 32767. Если ваш диапазон (Max-Min
) превышает RAND_MAX
, то с помощью этого метода вы получите несколько чисел с нулевой вероятностьюбыть возвращенным.
Это более тонко: предположим, что RAND_MAX
больше вашего диапазона, но не , что больше, скажем, RAND_MAX==1.5*/(Max-Min)
.В этом случае распределение результатов не будет равномерным: rand()
возвращает целое число в диапазоне [0, RAND_MAX
] (и каждое целое число в этом диапазоне должно быть равновероятным), но вы берете остатокделение с (Max-Min)
.Это означает, что числа в первой половине требуемого диапазона имеют в два раза больше вероятности возврата, чем остальные: они могут на самом деле приходить из первых и третьей трети диапазона rand()
, тогда какВторая половина необходимого диапазона может быть получена только из второй трети диапазона rand()
.
Что это значит для вас?
Вероятно, ничего.Если все, что вам нужно - это симулятор броска костей, вы можете без проблем использовать метод modulo, поскольку задействованный диапазон мал, а вторая проблема, несмотря на то, что все еще присутствует, почти не имеет значения:предположим, что ваш диапазон равен 3 и MAX_RAND
32767: от 0 до 32765, 0, 1 и 2 имеют одинаковую вероятность, но при увеличении до 32767 0 и 1 получают один потенциальный выход, что почти не имеет значения, поскольку они переходят от идеальногоВероятность 1/3 (10922/32766 = 0,333 ...) для каждого до 10922/32767 для 2 (~ 0,33332) и 10923/32767 (~ 0,33335) для 0 и 1 (при условии, что rand()
обеспечивает идеальное распределение.
Во всяком случае, для преодоления таких проблем достаточно часто используемым методом является "растяжение" диапазона rand()
в более широком диапазоне (или сжатие в меньшем диапазоне).) используя такой метод:
int GetRandomInt(int Min, int Max)
{
return (int)(((double)rand())/MAX_RAND*(Max-Min))+Min;
}
на основе эквивалентности rand():MAX_RAND=X:(Max-Min)
.Необходимо преобразовать в double, иначе целочисленное деление между rand()
и его максимальным значением всегда будет давать 0 (или 1 в редком случае rand()==MAX_RAND
);это можно сделать в целочисленной арифметике, выполняя продукт первым, если MAX_RAND невелик и диапазон тоже не слишком широк, в противном случае существует высокий риск переполнения.
Я подозреваю, что если выходной диапазон больше, чем диапазон rand()
, то «растяжение» и усечение значения fp (из-за преобразования в int) каким-то образом влияют на распределение, но тольколокально (например, в небольших диапазонах вы можете никогда не получить определенное число, но глобально распределение будет выглядеть нормально).
Обратите внимание, что этот метод помогает преодолеть диффузное ограничение генератора случайных чисел стандартной библиотеки C, т.е.низкая случайность младших битов возвращаемого значения, которые, кстати, используются при выполнении операции по модулю с небольшим выходным диапазоном.
Однако имейте в виду, что стандартная библиотека C для RNG является простой, которая стремится соответствовать «простым» статистическим правилам и поэтому легко предсказуема;его не следует использовать, когда требуются «серьезные» случайные числа (например, криптография).Для таких нужд существуют специальные библиотеки ГСЧ (например, часть ГСЧ Научной библиотеки GNU) или, если вам нужно действительно случайные вещи, есть несколько реальных службы случайных чисел (одна из самых известных - эта ), которые не используют математические псевдо-ГСЧ, но берут свои числа из реальных случайных источников (например, радиоактивного распада).