LINUX.ORG.RU

Использование rand()


0

1

Доброго времени суток. Столкнулся с проблемой, что при каждом запуске программы функция генерирует одну и ту же последовательность чисел.
Использую следующий код:

int rnd(int MAX)
{
    return (rand() % MAX) + 1;
}
(взято отсюда)

Как бы это для программы не очень приемлемо.
Посоветуйте что-нибудь, пожалуйста.
Спасибо!



Последнее исправление: GennDALF (всего исправлений: 1)

Ответ на: комментарий от GennDALF

Эта функция генерирует начальные данные для рандома. Причём случайность начальных данных должен обеспечить ты. Обычно бывает достаточно текущего времени.

melkor217 ★★★★★
()
Ответ на: комментарий от melkor217

с srand() все нормально, ругается на ltime.. я так понимаю это какая-то константа, она точно в cstdlib имеется? мб записывается по-другому?

>Причём случайность начальных данных должен обеспечить ты.
а можно подробнее? область для меня мало знакомая, поэтому я слегка «не в теме» ))

GennDALF
() автор топика
Ответ на: комментарий от GennDALF

блин, я туплю.. полистал time.h - вроде дошло, что и как..
скажит только, какого типа ltime обявлять?

GennDALF
() автор топика
Ответ на: комментарий от Boy_from_Jungle

Написал вот так:

int rnd(int MAX)
{
    time_t ltime = time(NULL);
    stime = ltime / 2;
    srand(stime);
    return (rand() % MAX) + 1;
}int)’

в итоге:
cnz.cpp:465: error: assignment of function ‘int stime(const time_t*)’
cnz.cpp:465: error: cannot convert ‘long int’ to ‘int(const time_t*)throw ()’ in assignment
cnz.cpp:466: error: invalid conversion from ‘int (*)(const time_t*)throw ()’ to ‘unsigned int’
cnz.cpp:466: error:   initializing argument 1 of ‘void srand(unsigned int)’

GennDALF
() автор топика
Ответ на: комментарий от unikoid

программа предполагается будет работать не только под никсом, а найти в винде /dev/urandom будет как минимум непросто ;)

GennDALF
() автор топика
Ответ на: комментарий от GennDALF

stime - функция из time.h. Подозреваю, что нужно использовать другое имя переменной вместо stime, предварительно объявив ее типа unsigned int. Иначе тут получается попытка присвоить функции значение типа time_t.

Если я не ошибаюсь, можно вообще сделать так:

int rnd(int MAX) 
{ 
    srand((unsigned int)time(NULL)/2); 
    return (rand() % MAX) + 1; 
}

unikoid ★★★
()
Ответ на: комментарий от dave

>Так делать нельзя. Вызов srand нужно вынести за пределы rnd. Вызывать его редко и осознанно.
Ну да, про это я в курсе, но я просто переделал код из оригинального поста так, чтобы он компилился. А так да, справедливое замечание.

unikoid ★★★
()

если вместо ЯП используются плюсы, то на кой хрен там сишная библиотека?

anonymous
()
Ответ на: комментарий от Reset

Тот, чью статью на линуксцентре читал ТС, тоже K&R в глаза не видел :)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от creepnee

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

Хорошо брать из /dev/urandom, если он есть конечно,
можно еще
getuid()
getgid()
getpid()

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

можно вместо time() использовать gettimeofday()

tv_sec << это как time()
tv_usec << а вот это число будет в микросекундах, не так предсказуемо как tv_sec )

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

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

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

> можно и не делить, просто если делать srand() от текущего времени, то данные будут не совсем случайны

Разве деление на 2 прибавит случайности? Скорее наоборот, т.к. размер сида уменьшается на 1 бит.

ntp
()
Ответ на: комментарий от ntp

почти любая операция с текущим временем добавляет случайности )
пусть даже и деление на 2 , 3 , 10 или значение от getpid()

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

Все таки я не понимаю. Допустим, time() вернет нам 64-битное как бы случайное число. Делением на 2 мы отбрасываем младший бит. Остается 63 случайных бита для сида. Откуда появляется новая информация?

Деление на getpid(), да, может добавить новой информации, т.к. pid тоже случайное число.

ntp
()
Ответ на: комментарий от ntp

>Допустим, time() вернет нам 64-битное как бы случайное число

на x86 - time_t = 32 бита

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

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

Надежнее вообще XOR'ить текущее время, pid процесса, объем свободной (или занятой) оперативки и т.п. (чем больше - тем лучше, хотя, по-моему, time+pid+mem достаточно надежный способ получения случайного srand'а).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

>чем больше - тем лучше

вот именно, что чем больше будет взято разных параметров и чем больше они будут преобразованы - тем лучше.

разные замены для /dev/(u)random в виде EGD например так и делают, собирают всякую разную системную статистику и несколько ее преобразовывают, в результате получаются практически непредсказуемые случайные данные для PRNG

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

> на x86 - time_t = 32 бита

Это не принципиально.

а вот какое-то его изменение для srand уже будет гораздо менее предсказуемым для стороннего наблюдателя не смотрящего в код программы.

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

Но все таки деление на 2 совершенно смешной способ для получения неразгадываемого сида. И я не вижу реальных преимуществ перед простым time(NULL), особенно если учесть, что деление на 2 дает менее разнообразные значения.

ntp
()
Ответ на: комментарий от ntp

А вы попробуйте запустить две версии одной и той же программы так, чтобы при делении time на 2 получилось одно и то же стартовое значение для rand ;)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Хотя, конечно, деление на 2 - не так уж и нужно. Толку-то от паузы в одну операцию сдвига вправо...

Eddy_Em ☆☆☆☆☆
()

Бросай монетку или игральную кость

int rand() 
{ 
    return 3; //или какое-там число выпало на кости 
}

Но профессионалы обычно используют именованные константы.

fool_anon
()
Ответ на: комментарий от Eddy_Em

Если исходники программы открыты, то никто не мешает подсмотреть, на что там делится time

creepnee
()
Ответ на: комментарий от Eddy_Em

> А вы попробуйте запустить две версии одной и той же программы так, чтобы при делении time на 2 получилось одно и то же стартовое значение для rand ;)

Это такой толстый троллинг или у вас с матчастью совсем плохо?

#include <stdio.h>
#include <time.h>

int main(void) {
	printf("%u\n", (unsigned int)time(NULL) / 2);
	return 0;
}
gcc main.c && for i in `seq 10`; do ./a.out; done

time(), обычно, имеет разрешение в 1 секунду. О том, что целочисленное деление на 2 не добавляет случайности я уже писал выше.

ntp
()
Ответ на: комментарий от ntp

Если кто-то еще не понял:

123456788 / 2 = 61728394
и
123456789 / 2 = 61728394

Делением time() на 2 вы делаете сид в 2 раза менее разнообразным.

ntp
()
Ответ на: комментарий от ntp

оно не добавляет, но оно сдвигает сид от значения текущего времени,
а вообще в теме уже не раз написано что текущего времени мало, нужно брать другую системную статистику, и если вернуться к времени, то gettimeofday() дает счетчик времени в микросекундах, его брать гораздо лучше, правда в плане переноса на windows многие функции будут непортабельны , так что лучше не заморачиваться и брать уже несколько байт с /dev/urandom, а если стойкость PRNG неважна - srand ((unsigned int)time (NULL)) , а уж делить его на что-то или нет - не столь уж и суть )

Sylvia ★★★★★
()
Ответ на: комментарий от Sylvia

Для стойкости и защиты от предсказывания деление на константу не защищает, особенно если есть дизассемблер.

Когда стойкость не нужна деление на 2 делает только хуже, как я уже показал выше.

Поэтому мне интересно зачем Boy_from_Jungle в первом сообщении привел именно вариант с делением 2.

ntp
()
Ответ на: комментарий от Eddy_Em

При чем тут gettimeofday? Вопрос был зачем в первом сообщении делят time() на 2.

ntp
()
Ответ на: комментарий от ntp

> Делением time() на 2 вы делаете сид в 2 раза менее разнообразным.

Чтобы прибавить рандомности, лучше умножить время на больше число. Желательно простое.

melkor217 ★★★★★
()
Ответ на: комментарий от GennDALF
-int rnd(int MAX) 
+void rnd_init(void) 
 { 
     time_t ltime = time(NULL); 
-    stime = ltime / 2; 
+    time_t stime = ltime / 2; 
     srand(stime); 
+}
+
+int rnd(int MAX)
+{
     return (rand() % MAX) + 1; 
-}int)’
+}

?

xydo ★★
()
Ответ на: комментарий от xydo
-  time_t ltime = time(NULL); 
-  time_t stime = ltime / 2;
+  struct timeval tv;
+  struct timezone tz;
+  gettimeofday(&tv, &tz);
-  srand(stime); 
+  srand(tv.usec ^ getpid());

получше будет.

А еще можно добавить сведений из /proc/loadavg, /proc/meminfo и т.п. или же инициировать srand значением из /dev/urandom.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от melkor217

void srand ( unsigned int ) ;

time(NULL) возвращает число достаточно близкое к пределу 32 бит,
т.е. даже на 2 его умножить без переполнения уже нельзя при sizeof(int) == 4

Sylvia ★★★★★
()

>Столкнулся с проблемой

Раньше с этой «проблемы» начинался не только любой курс про ГСЧ, но и любая статья в популярных журналах...

KRoN73 ★★★★★
()
Ответ на: комментарий от Sylvia

хотя не совсем, time_t на 32 битных системах - signed
т.е. для unsigned int (32 bit) его можно умножить на 2 без риска переполнения, переполнение будет если его умножить более чем на 3

Sylvia ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.