Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

Функция rand в цикле

#cpp #время #случайные_числа


double rnd()
{
    srand(time(0));
    return(rand() % 1000 / 1000.0);
}


Функцию выше вызываю в цикле, нужно совершить 1600 * 50000 прокручиваний. 
Все знакомы с функцией time(), цикл за секунду пробегает во много раз больше шагов,
чем 1. В итоге получаю докучи одинаковых чисел. 

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


Ответы

Ответ 1



srand(time(0)); нужно вызывать один раз в начале. Случайность будет нормальная. Постоянный вызов srand случайность не улучшает. Дополнение Если очень сильно упростить эти две функции (очень сильно), то код будет выглядеть где-то так: int seed = 0; // это начальное значение генератора int gen = 0; // начальное значения рандома int magick = 1234567; // это такая специальная магическая константа, я ее сам придумал:) // а вообще то есть целые исследования, которые определяют правильные константы // того же Кнута почитать можно void srand(int s) { seed = s; } int rand() { return func(gen, seed); } int func(int g, int s) { // эта функция на базе предыдущего значения вычисляет новое. // главное, что нужно понимать, что при одних и тех же значениях аргументов, // результат будет один и тот же. // реализация, приведенная ниже - это один с возможных вариантов. return (s * magick + g) % 0xFFFFFFFF; } Возникает вопрос - почему так сделано? Все очень просто. Сделать быстрый и качественный генератор достаточно сложно. Поэтому берут самые простые варианты. Усложнять подобные генераторы нет смысла - от этого их качество не улучшится, в лучшем случае останется таким же. Но обычно только ухудшается. Ещё одна причина, по которой это сделано - удобство отладки. Ведь seed определяет всю последовательность. И если его задать одинаковым, то rand будет выдавать одни и те же последовательности. Если нужна действительно случайная последовательность, то тут есть следующие варианты: купить специальное устройство, которое будет генерировать случайные числа. Вот например такое USB-токен «iBank 2 Key». Первое, что нашлось в гугле:) собрать такое устройство самостоятельно - статья на хабре. Там много интересного. купить специальный файл со случайными числами (да, такое продают. Я понимаю, что многие любители Линукса скажут, мол я с /dev/random сам нагенерю, но компании обещают хорошее качество.

Ответ 2



Это - т.наз. "часто возникающая ошибка". Если код программы содержит множественные попытки "перезапустить" генератор псевдослучайных чисел, используя в качестве seed-значения текущее значение времени, полученное через вызов time(NULL) ... srand(time(NULL)); ... то может получиться так, что такие вызовы srand будут следовать друг за другом очень быстро. Так быстро, что time(NULL) будет возвращать одно и то же значение времени. Это приведет к тому, что генератор псевдослучайных чисел будет постоянно перезапускаться с указанием одного и того же seed-значения. В результате последующие вызовы rand() будут порождать одну и ту же псевдослучайную последовательность. Например, вот такой код, будучи запущенным на выполнение, вероятнее всего выведет одну и ту же псевдослучайную последовательность два раза #include #include #include int main() { srand(time(NULL)); for (unsigned n = 20; n > 0; --n) printf("%d ", rand()); printf("\n"); srand(time(NULL)); for (unsigned n = 20; n > 0; --n) printf("%d ", rand()); printf("\n"); } Эта проблема также иногда рапортуется в следующем виде: "Под дебаггерром у меня в программе получаются разные случайные числа, а без дебаггера - одни и те же". Действительно, когда программа выполняется по шагам в интерактивном дебаггере, она выполняется "медленно" и между вызовами srand значение времени time(NULL) успевает измениться, что создает иллюзию того, что все работает "как задумано". Но стоит запустить программу в свободное выполнение, как проблема проявляется во всей красе. Если вам нужен вызов srand(time(NULL)) в вашей программе, то в большинстве случаев его достаточно сделать всего один-единственный раз на старте программы.

Комментариев нет:

Отправить комментарий