Рекомендуемый способ инициализации srand? - PullRequest
60 голосов
/ 27 ноября 2008

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

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

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

Я думал о некоторой математике pid / unixtime для получения int или, возможно, о чтении данных из /dev/urandom.

Спасибо!

EDIT

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

Ответы [ 14 ]

0 голосов
/ 06 декабря 2017

Пока ваша программа работает только в Linux (а ваша программа является исполняемым файлом ELF), вам гарантировано, что ядро ​​предоставляет вашему процессу уникальный случайный начальный вектор в векторе вспомогательных файлов ELF. Ядро дает вам 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.

0 голосов
/ 22 декабря 2014

Включите заголовок вверху вашей программы и напишите:

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;
}
0 голосов
/ 16 января 2013

Для тех, кто использует 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.

0 голосов
/ 10 мая 2011

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

int foo(char *p);

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

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

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

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

...