아래 코드는 간격 [1,100]에서 5 개의 의사 난수 목록을 생성하기위한 것입니다. 나는 시스템 시간을 유닉스 시간으로 반환 하는 default_random_engine
with를 시드합니다 . Microsoft Visual Studio 2013을 사용하여 Windows 7에서이 프로그램을 컴파일하고 실행하면 예상대로 작동합니다 (아래 참조). 그러나 g ++ 컴파일러로 Arch Linux에서 그렇게하면 이상하게 작동합니다.time(0)
Linux에서는 매번 5 개의 숫자가 생성됩니다. 마지막 4 개의 숫자는 각 실행마다 다르지만 (종종 그렇듯이) 첫 번째 숫자는 동일하게 유지됩니다.
Windows 및 Linux에서 5 개 실행의 출력 예 :
| Windows: | Linux:
---------------------------------------
Run 1 | 54,01,91,73,68 | 25,38,40,42,21
Run 2 | 46,24,16,93,82 | 25,78,66,80,81
Run 3 | 86,36,33,63,05 | 25,17,93,17,40
Run 4 | 75,79,66,23,84 | 25,70,95,01,54
Run 5 | 64,36,32,44,85 | 25,09,22,38,13
수수께끼에 덧붙여, 그 첫 번째 숫자는 Linux에서 주기적으로 1 씩 증가합니다. 위의 출력을 얻은 후 30 분 정도 기다렸다가 다시 1 번째 숫자가 변경되어 현재는 항상 26으로 생성되고 있음을 확인했습니다. 주기적으로 1 씩 계속 증가하여 현재 32에 해당하는 것 같습니다. 값이 time(0)
.
첫 번째 숫자가 실행 중에 거의 변경되지 않고 변경되면 1 씩 증가하는 이유는 무엇입니까?
코드. 5 개의 숫자와 시스템 시간을 깔끔하게 인쇄합니다.
#include <iostream>
#include <random>
#include <time.h>
using namespace std;
int main()
{
const int upper_bound = 100;
const int lower_bound = 1;
time_t system_time = time(0);
default_random_engine e(system_time);
uniform_int_distribution<int> u(lower_bound, upper_bound);
cout << '#' << '\t' << "system time" << endl
<< "-------------------" << endl;
for (int counter = 1; counter <= 5; counter++)
{
int secret = u(e);
cout << secret << '\t' << system_time << endl;
}
system("pause");
return 0;
}
답변
무슨 일이 일어나고 있는지 :
-
default_random_engine
libstdc ++ (GCC의 표준 라이브러리)minstd_rand0
에서는 간단한 선형 합동 엔진입니다.typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
-
이 엔진이 난수를 생성하는 방법은 x i + 1 = (16807x i + 0) mod 2147483647입니다.
-
따라서 시드가 1만큼 다르면 대부분의 경우 처음 생성 된 수는 16807만큼 다릅니다.
-
이 생성기의 범위는 [1, 2147483646]입니다. libstdc ++가
uniform_int_distribution
[1, 100] 범위의 정수에 매핑하는 방식 은 기본적으로 다음과 같습니다. generate a numbern
. 숫자가 2147483600보다 크지 않으면 반환합니다(n - 1) / 21474836 + 1
. 그렇지 않으면 새 번호로 다시 시도하십시오.대부분의 경우
n
16807 만 다른 두 s는이 절차에서 [1, 100]에서 동일한 수를 산출 한다는 것을 쉽게 알 수 있습니다. 실제로 생성 된 숫자가 약 21474836 / 16807 = 1278 초 또는 21.3 분마다 1 씩 증가 할 것으로 예상 할 수 있으며, 이는 귀하의 관찰과 매우 일치합니다.
MSVC의가 default_random_engine
있다 mt19937
이 문제가되지 않는다.
답변
는 std::default_random_engine
구현 정의이다. std::mt19937
또는 std::mt19937_64
대신 사용하십시오 .
또한 std::time
과 ctime
기능은 매우 정확하지 않습니다,에 정의 유형을 사용하는 <chrono>
대신 헤더를 :
#include <iostream>
#include <random>
#include <chrono>
int main()
{
const int upper_bound = 100;
const int lower_bound = 1;
auto t = std::chrono::high_resolution_clock::now().time_since_epoch().count();
std::mt19937 e;
e.seed(static_cast<unsigned int>(t)); //Seed engine with timed value.
std::uniform_int_distribution<int> u(lower_bound, upper_bound);
std::cout << '#' << '\t' << "system time" << std::endl
<< "-------------------" << std::endl;
for (int counter = 1; counter <= 5; counter++)
{
int secret = u(e);
std::cout << secret << '\t' << t << std::endl;
}
system("pause");
return 0;
}
답변
Linux에서 랜덤 함수는 확률 론적 의미에서 랜덤 함수가 아니라 의사 난수 생성기입니다. 그것은 씨앗으로 소금에 절이고 그 씨앗을 기반으로 생성되는 숫자는 의사 무작위이며 균일하게 분포됩니다. Linux 방식은 모집단의 정보를 사용하여 특정 실험을 설계 할 때 입력 정보를 수정하여 실험의 반복을 측정 할 수 있다는 장점이 있습니다. 최종 프로그램이 실제 테스트를 위해 준비되면 사용자에게 마우스를 움직이고 마우스 움직임을 몇 가지 키 입력과 혼합하고 시작 이후 마이크로 초 카운트를 추가하도록 요청하여 솔트 (시드)를 만들 수 있습니다. 마지막 전원을 켭니다.
Windows 난수 시드는 마우스, 키보드, 네트워크 및 시간 번호 모음에서 얻습니다. 반복 할 수 없습니다. 그러나 위에서 언급했듯이이 소금 값은 알려진 시드로 재설정 될 수 있습니다.
예, Linux에는 두 개의 난수 생성기가 있습니다. 하나, 기본값은 모듈로 32 비트이고 다른 하나는 모듈로 64 비트입니다. 선택은 정확도 요구 사항과 테스트 또는 실제 사용에 사용하려는 컴퓨팅 시간에 따라 달라집니다.