문제

C++에서 의사 난수 생성기를 초기화하는 '좋은' 방법이 필요합니다.내가 발견했다 기사 그 내용은 다음과 같습니다.

임의의 숫자를 생성하기 위해 SRAND는 일반적으로 실행 시간과 관련된 것과 같은 일부 독특한 값으로 초기화됩니다.예를 들어, 기능 시간 (헤더 C 시간에 선언)에 의해 반환 된 값은마다 다르므로 대부분의 무작위 요구에 충분히 독특합니다.

Unixtime은 내 응용 프로그램에 비해 충분히 독특하지 않습니다.이것을 초기화하는 더 좋은 방법은 무엇입니까?이식성이 있으면 보너스 포인트가 있지만 코드는 주로 Linux 호스트에서 실행됩니다.

나는 int를 얻기 위해 pid/unixtime 수학을 수행하거나 아마도 데이터를 읽을 생각이었습니다. /dev/urandom.

감사해요!

편집하다

예, 실제로는 초당 여러 번 애플리케이션을 시작하고 충돌이 발생했습니다.

도움이 되었습니까?

해결책

가장 좋은 대답은 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 랜드를 사용하지 마십시오. 대신 같은 것을 사용하십시오 /dev/random 또는 /dev/urandom 직접 (안에서 읽으십시오 int 직접 또는 그와 비슷한 것).

LIBC 랜드의 유일한 장점은 씨앗을 주어 주면 디버깅에 도움이되는 것이 예측 가능하다는 것입니다.

Windows :

srand(GetTickCount());

보다 더 나은 씨앗을 제공합니다 time() 밀리 초 이후.

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를 사용하여 시드합니다. 예를 들어 Libc ++ random_device accesses /dev /urandom에서 기본적으로 (대신 액세스 할 수있는 다른 파일을 제공 할 수 있지만)

다음으로 임의의 원인이 주어지면 분포에 대한 반복 호출이 1에서 100까지의 INT 분포를 생성 할 수 있도록 분포를 만듭니다. 그런 다음 분포를 반복적으로 사용하고 결과를 인쇄합니다.

가장 좋은 방법은 또 다른 의사로 된 숫자 생성기를 사용하는 것입니다. Mersenne Twister (및 Wichmann-Hill)가 저의 추천입니다.

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

모질라 코드의 unix_random.c 파일을 볼 수 있습니다. (Mozilla/Security/Freebl/...) FreeBL 라이브러리에 있어야합니다.

시스템 호출 정보 (PWD, NetStat ....)를 사용하여 임의 숫자에 대한 노이즈를 생성합니다. 대부분의 플랫폼을 지원하도록 작성되었습니다 (보너스 포인트 : D).

당신이 스스로에게 물어봐야 할 진짜 질문은 어떤 임의성 품질이 필요한지입니다.

LIBC Random은 a입니다 LCG

무작위성의 품질은 Srand와 함께 제공하는 입력이 낮을 것입니다.

단순히 다른 인스턴스에 다른 초기화가 있는지 확인하기 만하면 프로세스 ID (getPID), 스레드 ID 및 타이머를 혼합 할 수 있습니다. 결과를 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 and Boost :: date_time을 사용하여 위의 코드를 휴대용으로 만들 수 있습니다.

그만큼 c++11 Jonathan Wright의 최고 투표 포스트 버전 :

#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 함수가 발견되었습니다 여기.

편집 : 추가 조사에서 RAND_S는 Visual Studio의 좋은 대안 일 수 있습니다. 안전한 rand () 일뿐 아니라 완전히 다르며 Srand의 씨앗을 사용하지 않습니다. 나는 그것이 "더 안전한"Rand와 거의 동일하다고 추정했다.

rand_s를 사용하려면 stdlib.h가 포함되기 전에 #define _crt_rand_s를 잊지 마십시오.

프로그램이 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 기반 시스템으로 해석 될 수 있습니다. Linux 이외의 시스템에서 어떤 Aux 값이 구현되는지 잘 모르겠습니다.

프로그램 상단에 헤더를 포함시키고 다음을 작성하십시오.

srand(time(NULL));

당신의 프로그램에서 당신은 당신의 임의의 숫자를 선언하기 전에. 다음은 1에서 10 사이의 임의 숫자를 인쇄하는 프로그램의 예입니다.

#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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top