Рекомендуемый способ инициализации srand?

Мне нужен "хороший" способ для инициализации генератора псевдослучайных чисел в C++. Я нашел статьи о:

для генерации случайного типа чисел srand обычно инициализируется к некоторому отличительному значению, как те связано со временем выполнения. Для например, значение, возвращаемое время функции (объявлено в заголовке ctime) отличается каждую секунду, которая достаточно характерным для большинства randoming по необходимости.

Unixtime не достаточно для моего приложения. Какой лучший способ инициализировать это? Бонусные баллы, если он портативный, но код будет в основном работать на хостах Linux.

Я думал сделать некоторую математику pid / unixtime, чтобы получить int или, возможно, прочитать данные из /dev/urandom.

спасибо!

редактировать

Да, я на самом деле запускаю свое приложение несколько раз в секунду, и я столкнулся с столкновения.

14 ответов


лучший ответ-использовать материал случайных чисел Boost. Или если у вас есть доступ к C++11 использовать <random> заголовок.

но если мы говорим о rand() и srand()
Лучший способ-просто использовать time():

int main()
{
    srand(time(NULL));

    ...
}

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

каждый раз при запуске time() возвращает уникальное значение (если вы не запустите приложение несколько раз секунда.) В 32-битных системах, он будет только повторять каждые 60 лет.

Я знаю, вы не думаете, что время достаточно уникально, но мне трудно в это поверить. Но известно, что я ошибаюсь.

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

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

 int main()
 {
     struct timeval time; 
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     // Assuming you did not need quite that accuracy
     // Also do not assume the system clock has that accuracy.
     srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

     // The trouble here is that the seed will repeat every
     // 24 days or so.

     // If you use 100 (rather than 1000) the seed repeats every 248 days.

     // Do not make the MISTAKE of using just the tv_usec
     // This will mean your seed repeats every second.
 }

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

unsigned long seed = mix(clock(), time(NULL), getpid());

где Микс:

// http://www.concentric.net/~Ttwang/tech/inthash.htm
unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
    a=a-b;  a=a-c;  a=a^(c >> 13);
    b=b-c;  b=b-a;  b=b^(a << 8);
    c=c-a;  c=c-b;  c=c^(b >> 13);
    a=a-b;  a=a-c;  a=a^(c >> 12);
    b=b-c;  b=b-a;  b=b^(a << 16);
    c=c-a;  c=c-b;  c=c^(b >> 5);
    a=a-b;  a=a-c;  a=a^(c >> 3);
    b=b-c;  b=b-a;  b=b^(a << 10);
    c=c-a;  c=c-b;  c=c^(b >> 15);
    return c;
}

Если вам нужен лучший генератор случайных чисел, не используйте libc rand. Вместо этого просто используйте что-то вроде /dev/random или /dev/urandom напрямую (читай в int непосредственно из него или что-то в этом роде).

единственным реальным преимуществом libc rand является то, что, учитывая семя, оно предсказуемо, что помогает при отладке.


на windows:

srand(GetTickCount());

обеспечивает лучшее потомство, чем time() С момента его в миллисекундах.


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

http://en.wikipedia.org/wiki/Mersenne_twister


Я предлагаю вам посмотреть unix_random.C файл в коде mozilla. (думаю, это mozilla / безопасность / freebl/...) он должен быть в библиотеке freebl.

там он использует информацию о системном вызове ( например, pwd, netstat ....) для генерации шума для случайного числа; он написан для поддержки большинства платформ (что может дать мне бонусный балл :D ).


C++11 random_device

Если вам нужно приемлемое качество, то вы не должны использовать RAND() в первую очередь; вы должны использовать <random> библиотека. Он предоставляет множество отличных функций, таких как различные двигатели для различных компромиссов качества/размера/производительности, повторного входа и предопределенных распределений, чтобы вы не ошиблись. Он может даже обеспечить легкий доступ к недетерминированным случайным данным (например, /dev / random), в зависимости от вашего реализация.

#include <random>
#include <iostream>

int main() {
    std::random_device r;
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
    std::mt19937 eng(seed);

    std::uniform_int_distribution<> dist{1,100};

    for (int i=0; i<50; ++i)
        std::cout << dist(eng) << '\n';
}

eng является источником случайности, здесь встроенная реализация Mersenne twister. Мы сеем его с помощью random_device, который в любой приличной реализации будет недетерминированным RNG, и seed_seq для объединения более 32-бит случайных данных. Например, в libc++ random_device по умолчанию обращается к /dev/urandom (хотя вы можете дать ему другой файл для доступа).

Далее мы создаем распределение такое, что, учитывая источник случайность, повторные вызовы к распределению произведут равномерное распределение ints от 1 до 100. Затем мы переходим к многократному использованию дистрибутива и печати результатов.


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

libc random - это LCG

качество рандоме будет низкой, что введенные с srand.

Если вам просто нужно убедиться, что разные экземпляры будут иметь разные инициализации, вы можете смешать идентификатор процесса (getpid), идентификатор потока и таймер. Смешайте результаты с xor. Энтропии должно быть достаточно для большинства приложения.

пример :

struct timeb tp;
ftime(&tp);   
srand(static_cast<unsigned int>(getpid()) ^ 
static_cast<unsigned int>(pthread_self()) ^ 
static_cast<unsigned int >(tp.millitm));

для лучшего случайного качества используйте /dev / urandom. Вы можете сделать приведенный выше код переносимым с помощью boost::thread и boost:: date_time.


на c++11 версия топ проголосовал пост Джонатан Райт:

#include <ctime>
#include <random>
#include <thread>

...

const auto time_seed = static_cast<size_t>(std::time(0));
const auto clock_seed = static_cast<size_t>(std::clock());
const size_t pid_seed =
      std::hash<std::thread::id>()(std::this_thread::get_id());

std::seed_seq seed_value { time_seed, clock_seed, pid_seed };

...
// E.g seeding an engine with the above seed.
std::mt19937 gen;
gen.seed(seed_value);

#include <stdio.h>
#include <sys/time.h>
main()
{
     struct timeval tv;
     gettimeofday(&tv,NULL);
     printf("%d\n",  tv.tv_usec);
     return 0;
}

телевизор.tv_usec находится в микросекундах. Это должно быть приемлемое семя.


Предположим, у вас есть функция с подписью типа:

int foo(char *p);

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

  • полный результат clock_gettime (секунды и наносекунды) без выбрасывания низких битов - они самые ценные.
  • значение p приведение к uintptr_t.
  • адрес p приведение к uintptr_t.

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

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


для тех, кто использует Visual Studio, вот еще один способ:

#include "stdafx.h"
#include <time.h>
#include <windows.h> 

const __int64 DELTA_EPOCH_IN_MICROSECS= 11644473600000000;

struct timezone2 
{
  __int32  tz_minuteswest; /* minutes W of Greenwich */
  bool  tz_dsttime;     /* type of dst correction */
};

struct timeval2 {
__int32    tv_sec;         /* seconds */
__int32    tv_usec;        /* microseconds */
};

int gettimeofday(struct timeval2 *tv/*in*/, struct timezone2 *tz/*in*/)
{
  FILETIME ft;
  __int64 tmpres = 0;
  TIME_ZONE_INFORMATION tz_winapi;
  int rez = 0;

  ZeroMemory(&ft, sizeof(ft));
  ZeroMemory(&tz_winapi, sizeof(tz_winapi));

  GetSystemTimeAsFileTime(&ft);

  tmpres = ft.dwHighDateTime;
  tmpres <<= 32;
  tmpres |= ft.dwLowDateTime;

  /*converting file time to unix epoch*/
  tmpres /= 10;  /*convert into microseconds*/
  tmpres -= DELTA_EPOCH_IN_MICROSECS; 
  tv->tv_sec = (__int32)(tmpres * 0.000001);
  tv->tv_usec = (tmpres % 1000000);


  //_tzset(),don't work properly, so we use GetTimeZoneInformation
  rez = GetTimeZoneInformation(&tz_winapi);
  tz->tz_dsttime = (rez == 2) ? true : false;
  tz->tz_minuteswest = tz_winapi.Bias + ((rez == 2) ? tz_winapi.DaylightBias : 0);

  return 0;
}


int main(int argc, char** argv) {

  struct timeval2 tv;
  struct timezone2 tz;

  ZeroMemory(&tv, sizeof(tv));
  ZeroMemory(&tz, sizeof(tz));

  gettimeofday(&tv, &tz);

  unsigned long seed = tv.tv_sec ^ (tv.tv_usec << 12);

  srand(seed);

}

возможно, немного перебор, но хорошо работает для быстрых интервалов. функция gettimeofday нашел здесь.

Edit: при дальнейшем исследовании rand_s может быть хорошей альтернативой для Visual Studio, это не просто безопасный rand(), он полностью отличается и не использует семя из srand. Я предполагал, что он почти идентичен ранду, просто "безопаснее".

использовать rand_s просто не забудьте #define _CRT_RAND_S перед stdlib.h включен.


пока ваша программа работает только на Linux (и ваша программа является исполняемым файлом ELF), вам гарантируется, что ядро предоставляет вашему процессу уникальное случайное семя в векторе ELF aux. Ядро дает вам 16 случайных байтов, разных для каждого процесса, которые вы можете получить с помощью getauxval(AT_RANDOM). Использовать их для srand использовать просто int из них, такие как:

#include <sys/auxv.h>

void initrand(void)
{
    unsigned int *seed;

    seed = (unsigned int *)getauxval(AT_RANDOM);
    srand(*seed);
}

возможно, что это также переводится на другие системы на основе ELF. Я не уверен, что aux ценности реализуются в системах, отличных от Linux.


добавить заголовок в верхней части вашей программы, и написать:

srand(time(NULL));

в вашей программе, прежде чем объявить случайное число. Вот пример программы, которая выводит случайное число от одного до десяти:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
   //Initialize srand
   srand(time(NULL));

   //Create random number
   int n = rand() % 10 + 1;

   //Print the number
   cout << n << endl; //End the line

   //The main function is an int, so it must return a value
   return 0;
}