Ответ 1
Это на самом деле немного сложнее, чем большинство людей понимают:
int rand_lim(int limit) {
/* return a random number between 0 and limit inclusive.
*/
int divisor = RAND_MAX/(limit+1);
int retval;
do {
retval = rand() / divisor;
} while (retval > limit);
return retval;
}
Попытки, которые просто используют %
(или, что то же самое, /
), чтобы получить числа в диапазоне, почти неизбежно приводят к перекосу (то есть, некоторые числа будут генерироваться чаще, чем другие).
Что касается того, почему использование %
приводит к искаженным результатам: если нужный диапазон не является делителем RAND_MAX, перекос неизбежен. Если вы начнете с небольших чисел, то довольно легко понять, почему. Подумайте о том, чтобы взять 10 конфет (которые мы предположим, что вы не можете разрезать, разбить и т.д. На более мелкие кусочки) и попытаться равномерно распределить их между тремя детьми. Совершенно очевидно, что это невозможно сделать - если вы раздаете все конфеты, самое близкое, что вы можете получить, - это чтобы два ребенка получили три кусочка конфеты, а один из них - четыре.
У всех детей есть только один способ получить одинаковое количество конфет: убедитесь, что вы вообще не раздаете последний кусочек конфеты.
Чтобы связать это с приведенным выше кодом, давайте начнем с нумерации конфет от 1 до 10, а для детей - от 1 до 3. Начальное деление говорит, что, поскольку детей три, наш делитель - три. Затем мы вытаскиваем случайную конфету из ведра, смотрим на ее число, делим на три и даем ее этому ребенку - но если результат больше 3 (то есть мы выбрали конфету № 10), мы просто не раздайте это вообще - мы выбрасываем это и выбираем другую конфету.
Конечно, если вы используете современную реализацию C++ (т.е. ту, которая поддерживает C++ 11 или новее), вам обычно следует использовать один класс distribution
из стандартной библиотеки. Приведенный выше код наиболее близко соответствует std::uniform_int_distribution
, но стандартная библиотека также включает в себя uniform_real_distribution
, а также классы для ряда неоднородных распределений (Бернулли, Пуассон, обычный, может быть, пара других, которых я не помню в момент).