srand () - почему вы звоните только один раз?



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

565   7  

7 ответов:

зависит от того, что вы пытаетесь достичь.

рандомизация выполняется как функция, имеющая начальное значение, а именно семена.

Итак, для одного и того же семени вы всегда будете получать одну и ту же последовательность значений.

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

семена обычно берутся из текущего времени, которые являются секунды, как в time(NULL), поэтому, если вы всегда устанавливаете семя перед тем, как взять случайное число, вы получите тот же номер, пока вы вызываете комбинацию srand/rand несколько раз в ту же секунду.

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

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

кроме того, вы можете попытаться увеличить точность до микросекунд (минимизируя вероятность того же семени), требуется (sys/time.h):

struct timeval t1;
gettimeofday(&t1, NULL);
srand(t1.tv_usec * t1.tv_sec);

случайные числа на самом деле псевдослучайных. Сначала устанавливается семя, из которого каждый вызов rand получает случайное число и изменяет внутреннее состояние и это новое состояние используется в следующем rand звонок, чтобы получить другой номер. Потому что для генерации этих "случайных чисел" используется определенная формула, поэтому после каждого вызова rand вернет тот же номер из вызова. Например srand (1234); rand (); вернет то же значение. Инициализация один раз начальное состояние с начальным значением будет генерировать достаточно случайных чисел, поскольку вы не устанавливаете внутреннее состояние с srand, что делает числа более вероятными, чтобы быть случайными.

обычно мы используем time (NULL) возвращаемое значение секунд при инициализации начального значения. Скажи srand (time (NULL)); это в цикле. Тогда цикл может повторяться более одного раза в одну секунду, поэтому количество раз цикл повторяется внутри цикла в секунду rand вызов в цикле вернет тот же " случайный номер", который не является желательным. Инициализация его один раз при запуске программы установит семя один раз, и каждый раз , генерируется новый номер и изменяется внутреннее состояние, поэтому следующий вызов rand возвращает достаточно случайное число.

например этот код из http://linux.die.net/man/3/rand:

static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
    next = seed;
}

внутреннее состояние next объявляется глобальным. Каждый myrand вызов изменит внутреннее состояние и обновление это и возвращает случайное число. Каждый звонок myrand будет по-другому next значение поэтому метод будет возвращать различные номера каждый вызов.

посмотреть mysrand реализация; он просто устанавливает начальное значение, которое вы передаете next. Поэтому если вы установите next значение то же самое каждый раз перед вызовом rand он вернет то же самое случайное значение, из-за идентичной формулы, примененной к нему, что нежелательно, так как функция сделана быть случайным.

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

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

например, вы могли бы сделать:

int getRandomValue()
{
    srand(time(0));
    return rand();
}

и затем, если вы вызываете эту функцию повторно, так что time() возвращает те же значения в соседних вызовах вы просто получаете то же значение генерируется - это по дизайну.

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

подумайте об этом. rand() сделки с большой колодой карт, и каждый раз, когда вы называете его, все он делает, это выбрать следующую карту с верха колоды, значение и верните ту карту в низ колоды. (Да, это означает " случайный" последовательность повторится через некоторое время. Это же очень большая колода, хотя: обычно 4,294,967,296 карт.)

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

теперь, вы можете сказать: "Ладно, так как мне перетасовать колоду?- И ответ таков (по крайней мере, до rand и srand обеспокоены), нет способа перетасовки колоды.

так что srand делать? Основываясь на аналогии, которую я строил здесь, называя srand(n) это в основном как сказать: "вырезать палубу n карты сверху". Но подождите, еще одна вещь: это на самом деле возьмите еще одну совершенно новую колоду и разрежьте ее n карты сверху.

так что если вы называете srand(n), rand(),srand(n),rand(), ..., с тем же n каждый раз, вы не просто получите не очень случайную последовательность, вы на самом деле получите тот же номер обратно от rand() каждый раз. (Не обязательно тот же номер, который вы передали srand, но тот же номер назад от rand снова и снова.)

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

[П. С. Да, я знаю, в реальной жизни, когда вы покупаете новую колоду карт, это как правило в порядке, а не в случайном порядке. Чтобы аналогия здесь работала, я представляю, что каждая колода, которую вы покупаете в игровом магазине, находится в кажущемся случайном порядке, но точно такой же, казалось бы, случайный порядок, как и любая другая колода карт, которую вы покупаете в том же магазине. Вроде как одинаково перетасованные колоды карт они используются в турнирах по бриджу.]

srand семена генератор псевдослучайных чисел. Если вы назовете его более одного раза, вы будете повторно заполнить ГСЧ. И если вы вызываете его с тем же аргументом, он перезапустит ту же последовательность.

чтобы доказать это, если вы делаете что-то простое, как:

#include <cstdlib>
#include <cstdio>
int main() {
for(int i = 0; i != 100; ++i) {
        srand(0);
        printf("%d\n", rand());
    }
}

вы увидите одно и то же число, напечатанное 100 раз.

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

srand(time(NULL)-getpid());

этот метод делает ваше семя очень близким к случайному, поскольку нет способа угадать, в какое время ваш поток начался, и pid также будет отличаться.

1\кажется, что каждый раз, когда RAND () запускается, он будет устанавливать новое семя для следующего rand ().

2\Если srand () выполняется несколько раз, проблема в том, что если два запуска происходят за одну секунду(время (NULL) не изменяется), следующий rand() будет таким же, как rand () сразу после предыдущего srand ().

Comments

    Ничего не найдено.