문제
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)가 저의 추천입니다.
모질라 코드의 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;
}