Как мне уменьшить числа из 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;

Смотрите (для более подробной информации):

rand - C++ Reference

Другие также отметили, что это не даст вам наилучшего возможного распределения случайных чисел. Если такие вещи важны в вашем коде, вам придется сделать следующее:

int rawRand = (rand() * 1.0 / RAND_MAX) * 100;

РЕДАКТИРОВАТЬ

Три года спустя я делаю правку. Как уже упоминалось,rand()имеет много проблем. Очевидно, что я не могу рекомендовать его использование, когда есть лучшие альтернативы в будущем. Вы можете прочитать все о деталях и рекомендациях здесь:

rand () Считается вредным | GoingNative 2013

Ты можешь сделать

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

А пурист сказал бы, что ранд не производит случайных чисел.

Для вашей информации, качество случайных чисел измеряется путем взятия последовательности чисел, а затем вычисления математической вероятности того, что источник этой последовательности был случайным. Простой взлом с использованием остатка - очень плохой выбор, если вы ищете случайность.

rawRand % 101 даст [0-100] включительно.

Другие вопросы по тегам