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


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

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

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

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

спасибо!

EDIT

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

14 57

14 ответов:

лучший ответ-использовать материал Boost random number. Или если у вас есть доступ к 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());

где mix:

// 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 / security/ 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.

The 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. 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;
}