Как мне уменьшить числа из rand()?
Следующий код выводит случайное число каждую секунду:
int main ()
{
srand(time(NULL)); // Seeds number generator with execution time.
while (true)
{
int rawRand = rand();
std::cout << rawRand << std::endl;
sleep(1);
}
}
Как я могу уменьшить эти числа, чтобы они всегда были в диапазоне 0-100?
9 ответов
Если вы используете C++ и беспокоитесь о хорошем распространении, вы можете использовать TR1 C++ 11 <random>
,
#include <random>
std::random_device rseed;
std::mt19937 rgen(rseed()); // mersenne_twister
std::uniform_int_distribution<int> idist(0,100); // [0,100]
std::cout << idist(rgen) << std::endl;
Все приведенные примеры действительно дают плохо распределенные результаты. Часто выполняйте код и создавайте статистику, чтобы увидеть, как значения становятся искаженными.
Лучший способ создать реальное равномерное распределение случайных чисел в любом диапазоне [0, N] заключается в следующем (при условии, что rand
фактически следует равномерному распределению, что далеко не очевидно):
unsigned result;
do {
result = rand();
} while (result > N);
Конечно, этот метод медленный, но он дает хорошее распределение. Немного более разумный способ сделать это - найти наибольшее кратное N, которое меньше RAND_MAX
и используя это в качестве верхней границы. После этого можно смело брать result % (N + 1)
,
Для объяснения, почему метод наивного модуля является плохим и почему вышеупомянутое лучше, обратитесь к превосходной статье Жюльена об использованииrand
,
int rawRand = rand() % 101;
Смотрите (для более подробной информации):
Другие также отметили, что это не даст вам наилучшего возможного распределения случайных чисел. Если такие вещи важны в вашем коде, вам придется сделать следующее:
int rawRand = (rand() * 1.0 / RAND_MAX) * 100;
РЕДАКТИРОВАТЬ
Три года спустя я делаю правку. Как уже упоминалось,rand()
имеет много проблем. Очевидно, что я не могу рекомендовать его использование, когда есть лучшие альтернативы в будущем. Вы можете прочитать все о деталях и рекомендациях здесь:
Ты можешь сделать
cout << rawRand % 100 << endl; // Outputs between 0 and 99
cout << rawRand % 101 << endl; // outputs between 0 and 100
Для людей, понижающих голосование; обратите внимание, через минуту после того, как это было первоначально опубликовано, я оставил комментарий:
Из http://www.cplusplus.com/reference/clibrary/cstdlib/rand "Обратите внимание, что эта операция по модулю не генерирует действительно равномерно распределенное случайное число в промежутке (поскольку в большинстве случаев более низкие числа немного более вероятны), но это, как правило, хорошее приближение для коротких пролетов ".
С 64-разрядными числами и с использованием 100 чисел в качестве выходных данных числа 0-16 представлены с 1.00000000000000000455 % чисел (относительная точность к одинаково распределенному 1% примерно на 10-18), в то время как числа 17-99 представлены с 0,9999999999999999913 % от числа. Да, не идеально распределено, но очень хорошее приближение для небольших пролетов.
Также обратите внимание, где ОП запрашивает идентично распределенные номера? Мы знаем, что они используются в целях, где небольшие отклонения не имеют значения (например, что-либо кроме криптографии - и если они используют числа для криптографии, этот вопрос слишком наивен, чтобы они могли писать свою собственную криптографию).).
РЕДАКТИРОВАТЬ - Для людей, которые действительно заинтересованы в равномерном распределении случайных чисел, работает следующий код. Обратите внимание, что это не обязательно оптимально, так как для 64-битных случайных чисел требуется два вызова: rand()
один раз каждые 10^18 звонков.
unsigned N = 100; // want numbers 0-99
unsigned long randTruncation = (RAND_MAX / N) * N;
// include every number the N times by ensuring rawRand is between 0 and randTruncation - 1 or regenerate.
unsigned long rawRand = rand();
while (rawRand >= randTruncation) {
rawRand = rand();
// with 64-bit int and range of 0-99 will need to generate two random numbers
// about 1 in every (2^63)/16 ~ 10^18 times (1 million million times)
// with 32-bit int and range of 0-99 will need to generate two random numbers
// once every 46 million times.
}
cout << rawRand % N << stdl::endl;
Увидеть man 3 rand
- вам нужно масштабировать, разделив на RAND_MAX
чтобы получить диапазон [0, 1], после которого вы можете умножить на 100 для вашего целевого диапазона.
Для диапазона от мин до макс (включительно) используйте: int result = rand() % (max - min + 1) + min;
Некоторые люди опубликовали следующий код в качестве примера:
int rawRand = (rand() / RAND_MAX) * 100;
Это неверный способ решения проблемы, так как rand() и RAND_MAX являются целыми числами. В C++ это приводит к интегральному делению, которое усекает десятичные точки результатов. Если RAND_MAX >= rand(), результатом этой операции будет 1 или 0, что означает, что rawRand может быть только 0 или 100. Правильный способ сделать это будет следующим:
int rawRand = (rand() / static_cast<double>(RAND_MAX)) * 100;
Поскольку один из операндов теперь является двойным, используется деление с плавающей запятой, которое возвращает правильное значение в диапазоне от 0 до 1.
Как долго вы хотели бы получить ответ?
самое простое - преобразовать остаток от деления на 101:
int value = rawRand % 101;
Полупурист перемасштабировал бы, используя удваивается:
double dbl = 100 * ((double)rawRand / RAND_MAX);
int ivalue = (int)(dbl + 0.5); // round up for above 0.5
А пурист сказал бы, что ранд не производит случайных чисел.
Для вашей информации, качество случайных чисел измеряется путем взятия последовательности чисел, а затем вычисления математической вероятности того, что источник этой последовательности был случайным. Простой взлом с использованием остатка - очень плохой выбор, если вы ищете случайность.