<random>은 Linux에서 동일한 숫자를 생성하지만 Windows에서는 생성하지 않습니다.


90

아래 코드는 간격 [1,100]에서 5 개의 의사 난수 목록을 생성하기위한 것입니다. 나는 시스템 시간을 유닉스 시간으로 반환 하는 default_random_enginewith를 시드합니다 . 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;
}

3
무엇 sizeof(time_t)비교는 sizeof(default_random_engine::result_type)?
마크 랜섬

3
참고 default_random_engine두 플랫폼에서 완전히 다른 것입니다.
TC

1
여전히 임의의 BTW 일 수 있습니다.
Alec Teal

5
모든 프로그래머가 시간이 좋은 난수 생성기 시드라고 생각하는 단계를 거치나요?
OldFart 2015 년

6
@OldFart 네, 학계라고합니다.
Casey

답변:


141

무슨 일이 일어나고 있는지 :

  • default_random_enginelibstdc ++ (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 number n. 숫자가 2147483600보다 크지 않으면 반환합니다 (n - 1) / 21474836 + 1. 그렇지 않으면 새 번호로 다시 시도하십시오.

    대부분의 경우 n16807 만 다른 두 s는이 절차에서 [1, 100]에서 동일한 수를 산출 한다는 것을 쉽게 알 수 있습니다. 실제로 생성 된 숫자가 약 21474836 / 16807 = 1278 초 또는 21.3 분마다 1 씩 증가 할 것으로 예상 할 수 있으며, 이는 귀하의 관찰과 매우 일치합니다.

MSVC의가 default_random_engine있다 mt19937이 문제가되지 않는다.


36
그런 끔찍한 기본값을 선택하기 위해 GCC의 표준 라이브러리 개발자가 무엇을 소유했는지 궁금합니다.
CodesInChaos 2015-09-23

13
만들기가하지의 관련 그러나 맥 OS / 아이폰 OS가 툴체인 같은 끔찍한 임의 엔진을 사용 않다면 나도 몰라 @CodesInChaos, rand()7 %는 항상 0 반환
phuclv

7
@ LưuVĩnhPhúc 수정하지 않는 rand()것은 다소 이해할 수 있습니다 (절망적 인 유산 쓰레기입니다). 새로운 것을 위해 똥 계층 PRNG를 사용하는 것은 용납 할 수 없습니다. 표준은 "비교적, 비 숙련 적 및 / 또는 가벼운 사용을 위해 최소한 허용 가능한 엔진 동작을 제공"해야하므로이를 표준 위반이라고 생각합니다. 이 구현은 귀하의 rand % 7예제 와 같은 사소한 사용 사례에서도 치명적인 오류가 발생하므로 제공하지 않습니다 .
CodesInChaos

2
@CodesInChaos 수정 rand()이 정확히 이해하기 어려운 이유는 무엇 입니까? 아무도 그렇게 생각하지 않았기 때문일까요?
user253751

2
@immibis API가 너무 망가 져서 모든 문제를 해결하는 독립적 인 대체품을 사용하는 것이 좋습니다. 1) 알고리즘을 교체하는 것은 주요 변경 사항이므로 이전 프로그램에 대한 호환성 스위치가 필요할 수 있습니다. 2)의 씨앗 srand이 너무 작아서 고유 한 씨앗을 쉽게 생성 할 수 없습니다. 3) 호출자가 원하는 범위의 숫자로 어떻게 든 줄여야하는 구현 정의 된 상한을 가진 정수를 반환합니다. 제대로 수행되면 정상적인 API로 대체하는 것보다 더 많은 작업이 수행됩니다. rand()4) 전역 가변 상태를 사용합니다.
CodesInChaos 2015-09-24

30

std::default_random_engine구현 정의이다. std::mt19937또는 std::mt19937_64대신 사용하십시오 .

또한 std::timectime기능은 매우 정확하지 않습니다,에 정의 유형을 사용하는 <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;
}

3
의사 랜덤 변수 생성기를 시드 할 때 더 정확한 시간을 사용하는 것이 바람직합니까? 아마도 이것은 순진하지만 엔트로피를 도입하면 부정확성이 거의 바람직한 것처럼 느껴집니다. (정확하지 않아 잠재적 인 종자가 상당히 적다는 것을 의미하지 않는 한.)
Nat

15
std::random_device무작위 생성기를 시드하기 위해 current_time 대신 사용 하는 것이 좋습니다 . Random에 대한 cppreference 예제를 확인하십시오.
Aleksander Fular 2015-09-23

5
다른 사람이 당신의 시드를 추측하여 시퀀스를 재현하는 것을 원하지 않는다면, 정확도가 낮은 것이 더 무작위성과 같지 않습니다. 극단으로 가자 : 씨앗을 다음 날 (또는 연도?)로 반올림-> 추측이 쉽습니다. 펨토초 정밀도를 사용하여 -> 추측 많은이 할 ...
선형 가속기

2
@ChemicalEngineer의 단위 ctime는 1 초입니다. std::chrono구현 의 세분성 은 사용자 정의이며 기본값은 std::high_resolution_clock(Visual Studio에서는 typedef std::steady_clock) 나노초이지만 훨씬 더 작은 측정을 선택할 수 있으므로 훨씬 더 정확합니다.
Casey

2
@linac 암호화 속성을 원하면 적절한 prng (이 답변에 사용되지 않음)를 사용하십시오. 물론 시간 기반 시드도 약속 된 정확성에 상관없이 의문의 여지가 없습니다.
Cthulhu

-2

Linux에서 랜덤 함수는 확률 론적 의미에서 랜덤 함수가 아니라 의사 난수 생성기입니다. 그것은 씨앗으로 소금에 절이고 그 씨앗을 기반으로 생성되는 숫자는 의사 무작위이며 균일하게 분포됩니다. Linux 방식은 모집단의 정보를 사용하여 특정 실험을 설계 할 때 입력 정보를 수정하여 실험의 반복을 측정 할 수 있다는 장점이 있습니다. 최종 프로그램이 실제 테스트를 위해 준비되면 사용자에게 마우스를 움직이고 마우스 움직임을 몇 가지 키 입력과 혼합하고 시작 이후 마이크로 초 카운트를 추가하도록 요청하여 솔트 (시드)를 만들 수 있습니다. 마지막 전원을 켭니다.

Windows 난수 시드는 마우스, 키보드, 네트워크 및 시간 번호 모음에서 얻습니다. 반복 할 수 없습니다. 그러나 위에서 언급했듯이이 소금 값은 알려진 시드로 재설정 될 수 있습니다.

예, Linux에는 두 개의 난수 생성기가 있습니다. 하나, 기본값은 모듈로 32 비트이고 다른 하나는 모듈로 64 비트입니다. 선택은 정확도 요구 사항과 테스트 또는 실제 사용에 사용하려는 컴퓨팅 시간에 따라 달라집니다.


5
왜 시드 생성 알고리즘에 대해 이야기하고 있는지 잘 모르겠습니다. OP는 명확하게 시스템 시간을 시드로 사용합니다. 또한 몇 가지 참조를 추가 할 수 있습니까?collection of mouse, keyboard, network and time of day numbers
기본 로케일
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.