srand를 초기화하는 권장 방법?
C ++에서 의사 난수 생성기를 초기화하는 '좋은'방법이 필요합니다. 다음과 같은 기사 를 찾았 습니다 .
임의의 숫자를 생성하기 위해 srand는 일반적으로 실행 시간과 관련된 값과 같은 고유 한 값으로 초기화됩니다. 예를 들어 time 함수가 반환하는 값 (헤더 ctime에 선언 됨)은 매초마다 다르며, 이는 대부분의 무작위 요구에 충분히 구별됩니다.
Unixtime은 내 응용 프로그램에 대해 충분히 독특하지 않습니다. 이것을 초기화하는 더 좋은 방법은 무엇입니까? 이식 가능한 경우 보너스 포인트가 있지만 코드는 주로 Linux 호스트에서 실행됩니다.
나는 정수를 얻기 위해 pid / unixtime 수학을하거나 /dev/urandom
.
감사!
편집하다
예, 실제로 애플리케이션을 1 초에 여러 번 시작하고 충돌이 발생했습니다.
가장 좋은 대답은 Boost 난수를 사용하는 것입니다. 또는 C ++ 11에 액세스 할 수있는 경우 <random>
헤더를 사용하십시오 .
우리가 얘기한다면 rand()
그리고 srand()
가장 좋은 방법은 사용에 그냥 time()
:
int main()
{
srand(time(NULL));
...
}
호출 할 때마다가 아니라 프로그램 시작시이 작업을 수행해야합니다 rand()
.
시작할 때마다 time ()은 고유 한 값을 반환합니다 (애플리케이션을 1 초에 여러 번 시작하지 않는 한). 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.
}
이것은 자주 (1 초에 여러 번) 실행할 수있는 작은 명령 줄 프로그램에 사용 된 것입니다.
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()
밀리 초 단위 보다 더 나은 시드를 제공합니다 .
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
메르 센 트위스터의 내장 된 구현 인 임의성의 소스입니다. 임의의 구현에서 비 결정적 RNG가 될 random_device와 32 비트 이상의 임의 데이터를 결합하는 seed_seq를 사용하여 시드합니다. 예를 들어 libc ++에서 random_device는 기본적으로 / dev / urandom에 액세스합니다 (대신 액세스 할 다른 파일을 제공 할 수 있음).
다음으로 임의의 소스가 주어지면 분포를 반복적으로 호출하면 1에서 100까지 int의 균일 한 분포가 생성되도록 분포를 만듭니다. 그런 다음 분포를 반복적으로 사용하고 결과를 인쇄합니다.
가장 좋은 방법은 다른 의사 난수 생성기를 사용하는 것입니다. Mersenne twister (및 Wichmann-Hill)를 추천합니다.
http://en.wikipedia.org/wiki/Mersenne_twister
mozilla 코드에서 unix_random.c 파일을 볼 것을 제안합니다. (이것이 mozilla / security / freebl /이라고 생각합니다 ...) freebl 라이브러리에 있어야합니다.
시스템 호출 정보 (예 : pwd, netstat ....)를 사용하여 난수에 대한 노이즈를 생성합니다. 대부분의 플랫폼을 지원하도록 작성되었습니다 (보너스 포인트를 얻을 수 있습니다 : D).
스스로에게 물어봐야 할 진짜 질문은 어떤 무작위성 품질이 필요한지입니다.
libc random은 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 및 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가 포함되기 전에 _CRT_RAND_S를 #define하는 것을 잊지 마십시오.
As long as your program is only running on Linux (and your program is an ELF executable), you are guaranteed that the kernel provides your process with a unique random seed in the ELF aux vector. The kernel gives you 16 random bytes, different for each process, which you can get with getauxval(AT_RANDOM)
. To use these for srand
, use just an int
of them, as such:
#include <sys/auxv.h>
void initrand(void)
{
unsigned int *seed;
seed = (unsigned int *)getauxval(AT_RANDOM);
srand(*seed);
}
It may be possible that this also translates to other ELF-based systems. I'm not sure what aux values are implemented on systems other than Linux.
Include the header at the top of your program, and write:
srand(time(NULL));
In your program before you declare your random number. Here is an example of a program that prints a random number between one and ten:
#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;
}
참고URL : https://stackoverflow.com/questions/322938/recommended-way-to-initialize-srand
'Nice programing' 카테고리의 다른 글
C #에서 새 개체를 만들 때 {}가 ()처럼 작동합니까? (0) | 2020.11.29 |
---|---|
lodash : 객체에 배열 매핑 (0) | 2020.11.29 |
HTTP 406 및 415 오류 코드 (0) | 2020.11.29 |
git에서 서로 다른 디렉토리 계층을 가진 두 가지 분기를 병합하는 방법은 무엇입니까? (0) | 2020.11.29 |
PHP에서 세션과 쿠키의 차이점은 무엇입니까? (0) | 2020.11.29 |