2013-03-31 4 views
3

Я заметил, что, практикуя, простую консольную консольную программу. Когда я использую rand(), он дает мне одно и то же значение несколько раз подряд. Чем меньше числовой диапазон, тем больше проблема.rand() дает все то же значение

Например

for (i=0; i<10; i++) { 
    x = rand() % 20 + 1; 
    cout << x << ", "; 
} 

Даст мне 1, 1, 1, 2, 1, 1, 1, 1, 14, - есть определенно слишком много из них, не так ли? Обычно я получаю от одного до 4 нечетных номеров (остальное точно такое же, оно также может быть 11, 11, 11, 4, 11 ...)

Я что-то не так? Или rand() не настолько случайный, что я так и думал?
(Или это просто какая-то привычка от C#/Java, о которой я не знаю? Это часто бывает для меня тоже)

+2

'something% 1' всегда дает 0. Был ли он' rand()% 20 + 1'? –

+5

Вам нужно засеять случайное. Посмотрите на 'srand'. Люди часто используют 'time()' в качестве семени (хотя это не обязательно хорошая практика). – RageD

+1

@RageD: Вам нужно вызвать 'srand()' точно * один раз * перед любыми вызовами 'rand()'. –

ответ

4

Если я бегу, что код пару раз, я получаю различный результат. Конечно, не так разнообразен, как хотелось бы, но, по-видимому, не детерминирован (хотя, конечно, это так, поскольку rand() дает только псевдослучайные числа ...).

Однако, как вы относитесь к своим номерам, вы не получите равномерного распределения по сравнению с [1,20], и я думаю, это то, что вы ожидаете. Достичь этого довольно сложно, но никоим образом невозможно. Например, посмотрите на documentation for <random> at cplusplus.com - внизу есть программа витрины, которая генерирует равномерное распределение по [0,1). Чтобы получить это до [1,20], вы просто меняете входные параметры на генератор - это может дать вам равномерное распределение по любому интересующему вас диапазону.

Я сделал быструю проверку и назвал rand() миллион раз. Как видно из приведенного ниже результата, даже при очень больших размерах выборки, в распределении есть некоторая неоднородность. По мере того, как количество отсчетов переходит в бесконечность, линия будет (вероятно) сглаживаться, используя что-то вроде rand() % 20 + 1, дает вам распределение, которое принимает очень долгое время для этого. Если вы возьмете что-то еще (например, пример выше), ваши шансы лучше достичь равномерного распределения даже при довольно небольших размерах выборки.

1 million calls to rand()

Edit:
Я вижу некоторые другие публикации об использовании srand() семян генератора случайных чисел перед использованием. Это хороший совет, но в этом случае он не решит вашу проблему. Повторяю: Посев не является проблемой в этом случае.

Семена используются главным образом для контроля воспроизводимости выходных данных вашей программы. Если вы засеваете свое случайное число с постоянным значением (например, 0), программа будет давать один и тот же результат каждый раз, что полезно для тестирования того, что все работает так, как должно. При посеве чего-то непостоянным (текущее время является популярным выбором) вы гарантируете, что результаты будут различаться между различными прогонами программы.

Не созвониться srand() совсем не то же самое, что и вызов srand(1) по стандарту C++. Таким образом, вы будете получать одинаковые результаты каждый раз, когда запускаете программу, но в каждом прогоне вы получите вполне допустимую серию псевдослучайных чисел.

+0

Отмечу, что, хотя подход масштабирования намного лучше, чем 'rand()% 20', он все еще немного неоднороден, потому что количество значений, которые умножаются на каждый, будет немного отличаться. Чтобы понять, почему вы рассматриваете случай, когда вы кратно до диапазона, подобного по размеру, количеству битов в мантиссе вашего [0,1] диапазона. –

+1

@JackAidley: Конечно, но подход к масштабированию обычно «достаточно хорош» (т. Е. Это действительно только научные вычисления или алгоритмы, которые в большой степени зависят от того, насколько цифры как можно более случайны, что не может с ним поделать). Тем не менее, вы могли бы точно использовать равномерное распределение, с которым я связался без масштабирования, - и тогда это было бы равномерно =). Я обновлю свой ответ, чтобы отразить это. –

+0

Одной из основных целей 'srand' является возможность репликации результатов для устранения неожиданных результатов, что является точкой этого конкретного вопроса. SO увидела неожиданный паттерн. –

0

Вероятные проблемы в том, что вы используете один и тот же «случайный» случай, номера каждый раз и что любой int mod 1 равен нулю. Другими словами, (myInt % 1 == 0) всегда верен. Вместо %1 используйте % theBiggestNumberDesired.

Кроме того, введите ваши случайные числа srand. Используйте постоянное семя, чтобы убедиться, что вы получаете хорошие результаты. Затем измените семя, чтобы убедиться, что вы все еще получаете хорошие результаты. Затем используйте более случайное семя, как часы, чтобы соскочить дальше. Отпустите со случайным семенем.

+0

На самом деле, посев здесь не имеет значения. Не вызывать 'srand()' вообще эквивалентно вызову 'srand (1)', который даст одинаковый результат между разными прогонами, но он будет не менее случайным * в пределах одного и того же запуска * программы, чем посев с любым другой номер. –

+0

На самом деле, посев является актуальным. SO заметил неожиданный шаблон, когда использовалось семя по умолчанию. Использование другого семени позволяет проверить, есть ли какие-либо шаблоны, которые вы видите, случайным образом или фактически из-за системного смещения. –

+0

Ну, посев может быть релевантным как средство устранения неполадок, но он не объясняет шаблон, и он не «гарантирует, что вы получаете хорошие результаты» - результаты будут такими же хорошими (или плохими), не вызывая ' srand() ', но может быть проще узнать, почему они плохи *, если вы это сделаете ... –

2

Сначала нужно позвонить srand() и дать ему время для параметра для получения более качественных псевдослучайных значений.

Пример:

#include <iostream> 
#include <string> 
#include <vector> 
#include "stdlib.h" 
#include "time.h" 

using namespace std; 

int main() 
{ 
    srand(time(0)); 
    int x,i; 
    for (i=0; i<10; i++) { 
     x = rand() % 20 + 1; 
     cout << x << ", "; 
    } 
    system("pause"); 
    return 0; 
} 

Если вы не хотите какой-либо из сгенерированных чисел повторить и память не является проблемой вы можете использовать vector из Интс, перемешайте его в случайном порядке, а затем получить значения первые N ints.

Пример:

#include <iostream> 
#include <vector> 
#include <algorithm> 

using namespace std; 

int main() 
{ 
    //Get 5 random numbers between 1 and 20 
    vector<int> v; 
    for(int i=1; i<=20; i++) 
     v.push_back(i); 
    random_shuffle(v.begin(),v.end()); 
    for(int i=0; i<5; i++) 
     cout << v[i] << endl; 
    system("pause"); 
    return 0; 
} 
+1

Собственно, посев здесь не имеет значения. Не вызывать 'srand()' вообще эквивалентно вызову 'srand (1)', который даст одинаковый результат между разными прогонами, но он будет не менее случайным * в пределах одного и того же запуска * программы, чем посев с любым другой номер. –

+0

Ну, это даст одинаковые значения для каждого прогона, поэтому я предложил использовать время для посева. –

+1

Несомненно - но это не проблема, о которой говорит OP: P Вопрос в том, почему * такой же псевдослучайный номерный ряд * имеет столь низкую вариацию. Посев не имеет к этому никакого отношения. –

3

Похоже, вы попали modulo bias.

Масштабирование ваших случайных чисел в диапазоне с помощью % - это не очень хорошая идея. Это почти сносно, если вы уменьшите его до диапазона, который имеет мощность 2, но все еще довольно беден. На него в первую очередь влияют более мелкие биты, которые часто менее случайны со многими алгоритмами (и, в частности, rand()), и он сжимается до меньшего диапазона неравномерно, потому что диапазон вашего сокращения до неравномерного разделения диапазона ваших генератор случайных чисел. Чтобы уменьшить диапазон, который вы должны использовать разделение и петли, например, так:

// generate a number from 0 to range-1 
int divisor = MAX_RAND/(range+1); 
int result; 
do 
{ 
    result = rand()/divisor; 
} while (result >= range); 

Это не так неэффективно, как это выглядит, поскольку цикл почти всегда передается через только один раз. Кроме того, если вы когда-нибудь собираетесь использовать генератор для чисел, которые приближаются к MAX_RAND, вам понадобится более сложное уравнение для divisor, которое я не могу вспомнить с рук.

Также, rand() - очень плохой генератор случайных чисел, подумайте о том, чтобы использовать что-то вроде Mersenne Twister, если вы заботитесь о качестве своих результатов.

0

Примечание: rand() даст вам те же результаты, если вы запустите его в разных потоках без вызова srand(). Попробуйте это, чтобы убедиться:

vector<thread> workers; 
    for (int i = 0; i < 10; i++) 
     workers.push_back(std::thread([]{ cout << rand() << endl; })); 

    for (auto& th : workers) 
    { 
     if (th.joinable()) 
      th.join(); 
    } 
Смежные вопросы