mt19937 PRNG를 간결하고 이식 가능하며 철저하게 시드하는 방법은 무엇입니까?


112

<random>일반적으로 다음과 같은 코드와 함께 난수를 생성하기 위해 누군가 제안하는 많은 답변을 보는 것 같습니다 .

std::random_device rd;  
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);

일반적으로 이것은 다음과 같은 일종의 "거룩하지 않은 혐오"를 대체합니다.

srand(time(NULL));
rand()%6;

우리 는 낮은 엔트로피 를 제공 하고 예측 가능하며 최종 결과가 균일하지 않다고 주장함으로써 이전 방식을 비판 할 수 있습니다 .time(NULL)time(NULL)

그러나이 모든 것은 새로운 방식에 해당됩니다. 단지 더 빛나는 베니어를 가지고 있습니다.

  • rd()단일 unsigned int. 이것은 적어도 16 비트와 아마 32 비트를 가지고 있습니다. 이것은 MT의 19937 비트 상태를 시드하기에 충분하지 않습니다.

  • 사용 std::mt19937 gen(rd());gen()(32 비트로 시드하고 첫 번째 출력 확인)은 좋은 출력 분포를 제공하지 않습니다. 7과 13은 첫 번째 출력이 될 수 없습니다. 두 개의 씨앗이 0을 생산합니다. 12 개의 씨앗이 1226181350을 생산합니다. ( 링크 )

  • std::random_device고정 된 시드가있는 간단한 PRNG로 구현 될 수 있으며 때로는 구현됩니다. 따라서 모든 실행에서 동일한 시퀀스를 생성 할 수 있습니다. ( 링크 ) 이것보다 더 나쁘다 time(NULL).

더 나쁜 것은 앞서 언급 한 코드 조각이 포함 된 문제에도 불구하고 복사하여 붙여 넣기가 매우 쉽다는 것입니다. 이에 대한 일부 솔루션 은 모든 사람에게 적합하지 않을 수있는 거대한 라이브러리 를 확보 해야합니다.

이에 비추어 내 질문은 어떻게 C ++에서 mt19937 PRNG를 간결하고 이식 가능하며 철저하게 시드 할 수 있습니까?

위의 문제를 감안할 때 좋은 대답 :

  • mt19937 / mt19937_64를 완전히 시드해야합니다.
  • 엔트로피 에만 의존 std::random_device하거나 time(NULL)소스로 의존 할 수 없습니다 .
  • Boost 또는 다른 라이브러리에 의존해서는 안됩니다.
  • 답변에 복사하여 붙여 넣으면 멋지게 보이도록 적은 수의 줄에 맞아야합니다.

생각

  • 내 현재 생각은 , 주소 공간 무작위 화 에서 파생 된 값 및 하드 코딩 된 상수 (배포 중에 설정할 수 있음 std::random_device)로 출력을 매시업 (아마도 XOR을 통해)하여 엔트로피에서 최선을 다하는 샷을 얻을 수 있다는 것입니다.time(NULL)

  • std::random_device::entropy() 무엇을할지 안할지에 대한 좋은 지표를 제공 하지 않습니다std::random_device .


24
@Fabien : 그것에 대해 휴대용은 무엇입니까? 이것은 Linux 질문이 아니라 C ++ 질문입니다.
궤도

6
내 개인적인 생각은 아마도 값에서 얻을 수있는 것이 었습니다 std::random_device, time(NULL)다음, 함수 주소 최선의 노력을 엔트로피 소스의 종류를 생산하기 위해 함께 약하게.
Richard

5
does_random_device_actually_work ()와 같은 기능이 있으면 좋을 것입니다. 그래야 적어도 우아하게 저하되거나 사용자에게 경고 나 오류가 발생할 수 있습니다.

4
적절한 솔루션은 짧지 않고 짧은 솔루션은 적절하지 않습니다. 내 seed11 라이브러리 에서 사용하는 내 접근 방식 은 기본적으로 std::random_device프로그램을 실행할 플랫폼에서 제대로 구현 하고 시드 생성기를 만드는 도우미 함수를 제공하는 것입니다 ( seed11::make_seeded<std::mt19937>())
milleniumbug

5
곁에 : 두 번째 총알은 새로운 것을 추가하지 않습니다. 12 번 나타나는 값을 찾은 것은 놀라운 일이 아닙니다. 2 ^ 32 개의 독립적이고 균일 한 랜덤 샘플 이 있다고 가정 할 때 정확히 12 번 나타나는 값이 세 개를 약간 넘을 것으로 예상해야 합니다.

답변:


58

가장 큰 결함 std::random_device은 CSPRNG를 사용할 수없는 경우 결정 론적 폴 백이 허용된다는 것입니다. 이것은 std::random_device생성 된 바이트가 결정적 일 수 있으므로를 사용하여 PRNG를 시드하지 않는 좋은 이유 입니다. 안타깝게도 이것이 언제 발생하는지 알아 내거나 저품질 난수 대신 실패를 요청하는 API를 제공하지 않습니다.

즉, 완전히 이식 가능한 솔루션 은 없지만 적절한 최소한의 접근 방식이 있습니다. CSPRNG ( sysrandom아래에 정의 됨) 주위에 최소 래퍼를 사용 하여 PRNG를 시드 할 수 있습니다.

윈도우


CryptGenRandomCSPRNG를 사용할 수 있습니다 . 예를 들어 다음 코드를 사용할 수 있습니다.

bool acquire_context(HCRYPTPROV *ctx)
{
    if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
        return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
    }
    return true;
}


size_t sysrandom(void* dst, size_t dstlen)
{
    HCRYPTPROV ctx;
    if (!acquire_context(&ctx)) {
        throw std::runtime_error("Unable to initialize Win32 crypt library.");
    }

    BYTE* buffer = reinterpret_cast<BYTE*>(dst);
    if(!CryptGenRandom(ctx, dstlen, buffer)) {
        throw std::runtime_error("Unable to generate random bytes.");
    }

    if (!CryptReleaseContext(ctx, 0)) {
        throw std::runtime_error("Unable to release Win32 crypt library.");
    }

    return dstlen;
}

유닉스 유사


많은 유닉스 계열 시스템에서 가능하면 / dev / urandom을 사용해야 합니다 (POSIX 호환 시스템에 존재한다고 보장 할 수는 없지만).

size_t sysrandom(void* dst, size_t dstlen)
{
    char* buffer = reinterpret_cast<char*>(dst);
    std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
    stream.read(buffer, dstlen);

    return dstlen;
}

다른


CSPRNG를 사용할 수없는 경우 std::random_device. 그러나 다양한 컴파일러 (특히 MinGW)가 PRNG 로 구현하기 때문에 가능하면 이것을 피할 것입니다 (사실 매번 동일한 시퀀스를 생성하여 사람에게 적절하게 무작위가 아니라는 것을 경고합니다).

파종


이제 최소한의 오버 헤드로 조각을 얻었으므로 원하는 비트의 임의 엔트로피를 생성하여 PRNG를 시드 할 수 있습니다. 이 예에서는 (분명히 불충분 한) 32 비트를 사용하여 PRNG를 시드하고이 값을 늘려야합니다 (CSPRNG에 따라 다름).

std::uint_least32_t seed;    
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);

부스트 비교


소스 코드를 간략히 살펴본 후 boost :: random_device (진정한 CSPRNG)에 대한 유사점을 볼 수 있습니다 . Boost는 MS_DEF_PROVWindows에서 사용 하며 PROV_RSA_FULL. 빠진 유일한 것은 암호화 컨텍스트를 확인하는 것입니다 CRYPT_VERIFYCONTEXT. * Nix에서 Boost는 /dev/urandom. IE,이 솔루션은 이식 가능하고 잘 테스트되었으며 사용하기 쉽습니다.

Linux 전문화


보안을 위해 간결함을 희생하려는 경우 getrandomLinux 3.17 이상 및 최신 Solaris에서 탁월한 선택입니다. 커널이 부팅 후 아직 CSPRNG를 초기화하지 않은 경우 차단된다는 점을 제외하면 getrandom과 동일하게 작동 /dev/urandom합니다. 다음 스 니펫은 Linux getrandom를 사용할 수 있는지 여부를 감지 하고 그렇지 않은 경우 /dev/urandom.

#if defined(__linux__) || defined(linux) || defined(__linux)
#   // Check the kernel version. `getrandom` is only Linux 3.17 and above.
#   include <linux/version.h>
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
#       define HAVE_GETRANDOM
#   endif
#endif

// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
#   include <sys/syscall.h>
#   include <linux/random.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#elif defined(_WIN32)

// Windows sysrandom here.

#else

// POSIX sysrandom here.

#endif

OpenBSD


마지막 경고가 하나 있습니다. 최신 OpenBSD에는 /dev/urandom. 대신 getentropy 를 사용해야 합니다.

#if defined(__OpenBSD__)
#   define HAVE_GETENTROPY
#endif

#if defined(HAVE_GETENTROPY)
#   include <unistd.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = getentropy(dst, dstlen);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#endif

다른 생각들


암호 학적으로 안전한 임의 바이트가 필요한 경우 fstream을 POSIX의 버퍼링되지 않은 열기 / 읽기 / 닫기로 대체해야합니다. 모두 있기 때문이다 basic_filebufFILE표준 할당을 통해 할당 (따라서 메모리에서 닦여 생략)한다 내부 버퍼를 포함한다.

다음으로 변경 sysrandom하면 쉽게 수행 할 수 있습니다 .

size_t sysrandom(void* dst, size_t dstlen)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        throw std::runtime_error("Unable to open /dev/urandom.");
    }
    if (read(fd, dst, dstlen) != dstlen) {
        close(fd);
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    close(fd);
    return dstlen;
}

감사


FILE버퍼링 된 읽기 사용 을 지적한 Ben Voigt에게 특별히 감사드립니다 . 따라서 사용해서는 안됩니다.

나는 또한 언급 한 Peter Cordes getrandom와 OpenBSD의 /dev/urandom.


11
이것이 내가 과거에 한 일이지만, 적어도 하나의 질문은 WTF가 이러한 플랫폼의 라이브러리 작성자가 우리를 위해 이것을 할 수 없다는 것입니다. 파일 액세스 및 스레드 (예 :)가 라이브러리 구현에 의해 추상화 될 것으로 예상하는데, 난수 생성은 어떻습니까?

2
여기에 OP :이 답변이 시드를 조금 더 잘 보여 주면 좋을 것입니다. 가능한 한 많은 기술적 해석이나 코더 부분에 대한 생각을 요구하지 않고 내 질문에 게시 한 간단한 예제보다 더 잘하는 복사 붙여 넣을 수있는 코드를 생성하는 답변을 기대하고 있습니다.
Richard

4
/dev/randomRNG를 시드하는 데 더 나은 선택 이라고 생각 했지만 사용 가능한 엔트로피가 낮기 때문에 차단되는 경우에도 /dev/urandom여전히 계산적으로 안전한 것으로 간주/dev/random 되므로 urandom일회성 패드를 제외한 모든 항목에 권장되는 선택입니다. unix.stackexchange.com/questions/324209/… 도 참조하십시오 . urandom하지만 부팅 후 아주 일찍 부터 예측 가능한 시드를 조심하십시오 .
Peter Cordes

2
Linux의 getrandom(2)시스템 호출은 /dev/urandom커널의 임의성 소스가 아직 초기화되지 않은 경우 차단된다는 점을 제외하면 열기 및 읽기와 같습니다 . 나는 이것이 다른 경우에 차단하지 않고 조기 부팅 낮은 품질의 임의성 문제에서 당신을 구한다고 생각합니다 /dev/random.
Peter Cordes

1
@PeterCordes는 물론 가능하면 훌륭한 옵션입니다. 그러나 /dev/urandom일반적으로 작동 하는 BSD 또는 기타 * Nixes에서는 작동하지 않습니다 . 이에 대한 파이썬 메일 링리스트 토론은 제가 일반적으로 구독하는 것입니다 : bugs.python.org/issue27266
Alexander Huszagh

22

어떤 의미에서 이것은 이식 가능하게 할 수 없습니다. 즉, PRNG를 시드 할 임의의 소스가없는 C ++ (예 : 기계 시계를 결정적으로 단계적으로 이동하고 "결정된"I / O를 사용하는 시뮬레이터)를 실행하는 유효한 완전 결정적 플랫폼을 생각할 수 있습니다.


1
@kbelder : 1. 사용자가 사람이라고 누가 말합니까? 2. 모든 프로그램은 사용자 상호 작용을 가지고 있고 당신은 확실히 사용자가 ... 주변에 항상 거기 가정 할 수 없다
einpoklum

8
이 답변에 감사 드리지만 프로그램이 합리적인 최선의 노력을 기울여야한다고 생각합니다.
Richard

3
@Richard 동의하지만 문제는 C ++ 표준 작성자가 이러한 종류의 기괴한 상황을 수용해야한다는 것입니다. 그렇기 때문에 괜찮은 결과를 얻을 수 있지만 컴파일러는 기능적으로 쓸모없는 것을 돌려 주더라도 여전히 표준을 준수 할 수 있습니다. -따라서 제한 사항 ( "짧고 다른 라이브러리에 의존 할 수 없음")은 효과적으로 플랫폼 별 / 컴파일러 별 특수 케이스가 필요하므로 모든 응답을 배제합니다. (예 : Boost가 잘하는 것)
RM

2
@Richard가 설명 하는 것은 더 나은 작업을 수행 할 수있는 휴대용 방법이 없기 때문에 표준에서 얻은 것을 얻는다는 것입니다. 당신은 (고귀한 목표 인) 더 잘 할 경우 일부 크거나 :) 혐오의 적은 양을 받아 들여야 할 것이다
홉스

1
@Richard : 때로는 유용하지 않은 표준 호환 C ++ 구현을 만드는 것이 가능하다는 사실을 받아 들여야합니다. 사람들이 중요한 모든 것에 사용하는 구현 유용하도록 설계 되었기 때문에 때때로 "정확한 구현은 합리적인 일을 할 것입니다"와 같은 주장을 가지고 살아야합니다. 나는 std::random_device그것이 그 범주에 속하기를 바 랐을 것이지만, 일부 실제 구현이 고정 시드 PRNG를 사용하는 경우는 분명히 그렇지 않습니다! 그것은 einpoklum의 주장을 넘어선 다.
Peter Cordes

14

a를 사용하고 std::seed_seqAlexander Huszagh의 엔트로피를 얻는 방법을 사용하여 생성기에 필요한 상태 크기 이상으로 채울 수 있습니다 .

size_t sysrandom(void* dst, size_t dstlen); //from Alexander Huszagh answer above

void foo(){

    std::array<std::mt19937::UIntType, std::mt19937::state_size> state;
    sysrandom(state.begin(), state.length*sizeof(std::mt19937::UIntType));
    std::seed_seq s(state.begin(), state.end());

    std::mt19937 g;
    g.seed(s);
}

표준 라이브러리 의 UniformRandomBitGenerator 에서 SeedSequence 를 채우거나 만드는 적절한 방법이 있다면 적절하게 시드를 사용 하는 것이 훨씬 간단 할 것입니다.std::random_device



C ++ 표준에는 아무것도 없으며 seed_seq에서 시드 할 때 난수 생성기가 전체 배열을 사용하도록 보장 할 수 없습니다. 이 방법은 과학적 시뮬레이션 및 분명히 암호화에 rng를 사용하는 경우 실패로 이어집니다. 이것에 대한 유일한 사용 사례는 비디오 게임을 무작위 화하는 것이지만, 과잉이 될 것입니다.
Kostas

5

내가 작업중인 구현 state_sizemt19937PRNG 의 속성을 활용 하여 초기화시 제공 할 시드 수를 결정합니다.

using Generator = std::mt19937;

inline
auto const& random_data()
{
    thread_local static std::array<typename Generator::result_type, Generator::state_size> data;
    thread_local static std::random_device rd;

    std::generate(std::begin(data), std::end(data), std::ref(rd));

    return data;
}

inline
Generator& random_generator()
{
    auto const& data = random_data();

    thread_local static std::seed_seq seeds(std::begin(data), std::end(data));
    thread_local static Generator gen{seeds};

    return gen;
}

template<typename Number>
Number random_number(Number from, Number to)
{
    using Distribution = typename std::conditional
    <
        std::is_integral<Number>::value,
        std::uniform_int_distribution<Number>,
        std::uniform_real_distribution<Number>
    >::type;

    thread_local static Distribution dist;

    return dist(random_generator(), typename Distribution::param_type{from, to});
}

크기와 범위 std::random_device::result_type가 다를 수 std::mt19937::result_type있으므로 개선의 여지가 있다고 생각 합니다.

std :: random_device 에 대한 참고 사항 .

C++11(/14/17)표준 에 따르면 :

26.5.6 클래스 random_device [ rand.device ]

2 구현 제한으로 인해 비 결정적 난수 생성이 방지되는 경우 구현시 난수 엔진을 사용할 수 있습니다.

이는 구현이 일부 제한에 의해 결정적 값을 생성 하지 못하게하는 경우 에만 결정적 값을 생성 할 수 있음을 의미합니다 .

MinGW컴파일러 는 운영 체제에서 쉽게 사용할 수 있음에도 불구하고 에서 비 결정적 값을 Windows제공하지 않는 것으로 유명 합니다. 그래서 나는 이것을 버그라고 생각하며 구현과 플랫폼에서 흔히 발생하지 않을 것입니다.std::random_device


1
이것은 MT 상태를 채울 수 있지만 여전히에 전적으로 의존 std::random_device하므로 이로 인한 문제에 취약합니다.
Richard

1
나는 질문에서 충분히 명확하게 언급했다고 생각합니다. 그래도 명확히 / 토론하게되어 기쁩니다.
Richard

2
@Richard 실제로 합리적인 구현이 아닌 실제 시스템이 std::random_device있습니까? 나는 표준이 대체를 허용한다는 것을 알고 PRNG있지만 사용하는 모든 장치 C++에 비 결정적 임의 소스가 있음을 요구하기가 어렵 기 때문에 스스로를 덮는 것이라고 생각합니다 . 그리고 그들이 그렇지 않다면 어쨌든 그것에 대해 무엇을 할 수 있습니까?
Galik

5
@AlexanderHuszagh 잘 모르겠습니다. 내 의도는 내 "휴대용 솔루션"을 장치에 종속되도록 만드는 것입니다 . 장치가 비 결정적 생성기를 지원하면 std::random_device. 이것이 표준의 정신이라고 믿습니다. 그래서 나는 수색을했고이 MinGW점에서 깨어진 것만 찾을 수 있습니다 . 내가 발견 한 다른 어떤 것도이 문제를보고하는 사람이없는 것 같습니다. 따라서 내 라이브러리에서 단순히 MinGW지원되지 않음 으로 표시 했습니다. 더 넓은 문제가 있었다면 다시 생각할 것입니다. 지금은 그 증거가 보이지 않습니다.
Galik

5
MinGW가 std::random_device플랫폼의 임의성 기능을 제공하지 않는 형태로 제공함으로써 모든 사람을 망치고 있다는 사실에 정말 실망합니다 . 저품질 구현은 기존 API의 목적을 무효화합니다. 그들이 작동 할 때까지 전혀 구현하지 않았다면 IMO가 더 좋을 것입니다. (또는 더 좋은 방법은 API가 고품질 임의성을 사용할 수없는 경우 실패를 요청하는 방법을 제공했다면 MinGW는 게임 등에 대해 다른 시드를 제공하면서 보안 위험을 피할 수 있습니다.)
Peter Cordes

2

보안이 필요하지 않다고 가정하면 시간을 사용하여 시딩하는 데 아무런 문제가 없습니다 (그리고 이것이 필요하다고 말하지 않았 음). 통찰력은 해싱을 사용하여 비 무작위성을 수정할 수 있다는 것입니다. 나는 이것이 무거운 Monte Carlo 시뮬레이션을 포함한 모든 경우에 적절하게 작동한다는 것을 발견했습니다.

이 접근 방식의 한 가지 좋은 특징은 실제로 무작위가 아닌 다른 시드 세트에서 초기화로 일반화된다는 것입니다. 예를 들어 각 스레드가 자체 RNG (스레드 안전성을 위해)를 갖도록하려면 해시 된 스레드 ID를 기반으로 초기화하면됩니다.

다음은 내 코드베이스 에서 추출한 SSCCE입니다 (단순함을 위해 일부 OO 지원 구조 가 제거됨 ).

#include <cstdint> //`uint32_t`
#include <functional> //`std::hash`
#include <random> //`std::mt19937`
#include <iostream> //`std::cout`

static std::mt19937 rng;

static void seed(uint32_t seed) {
    rng.seed(static_cast<std::mt19937::result_type>(seed));
}
static void seed() {
    uint32_t t = static_cast<uint32_t>( time(nullptr) );
    std::hash<uint32_t> hasher; size_t hashed=hasher(t);
    seed( static_cast<uint32_t>(hashed) );
}

int main(int /*argc*/, char* /*argv*/[]) {
    seed();
    std::uniform_int_distribution<> dis(0, 5);
    std::cout << dis(rng);
}

1
나는 당신이 안전 할 필요가 없다면, 시간에 따른 씨 뿌리기가 실제로 충분할 것이라는 당신의 요점에 동의합니다. 하지만 나머지 답변에 동의 할 수 없습니다. 시간의 해시로 시드하는 것은 시간 자체로 시드하는 것보다 낫지 않습니다.
DW

@DW 경험적으로 훨씬 낫습니다. 그 이유는 (: 시드와 자신이 시도 해시 값의 매우 넓은 범위의 불연속 및 스팬이다 12그들에 의해 생성 된 플로트의 시퀀스가 정말로 어긋나 기 동안 소요 관찰).
imallett

그게 왜 중요한지 모르겠습니다. 우리는 한 번에 하나의 시드에서만 실행됩니다. 시드 (시드의 엔트로피)에 대해 가능한 값의 공간은 어느 쪽이든 동일합니다. 해싱은 엔트로피를 증가시키지 않습니다. 해싱이 더 나은 이유를 설명하기 위해 질문을 편집 할 수 있습니까?
DW

0

질문에 대한 내 자신의 찌르기입니다.

#include <random>
#include <chrono>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <iostream>

uint32_t LilEntropy(){
  //Gather many potential forms of entropy and XOR them
  const  uint32_t my_seed = 1273498732; //Change during distribution
  static uint32_t i = 0;        
  static std::random_device rd; 
  const auto hrclock = std::chrono::high_resolution_clock::now().time_since_epoch().count();
  const auto sclock  = std::chrono::system_clock::now().time_since_epoch().count();
  auto *heap         = malloc(1);
  const auto mash = my_seed + rd() + hrclock + sclock + (i++) +
    reinterpret_cast<intptr_t>(heap)    + reinterpret_cast<intptr_t>(&hrclock) +
    reinterpret_cast<intptr_t>(&i)      + reinterpret_cast<intptr_t>(&malloc)  +
    reinterpret_cast<intptr_t>(&LilEntropy);
  free(heap);
  return mash;
}

//Fully seed the mt19937 engine using as much entropy as we can get our
//hands on
void SeedGenerator(std::mt19937 &mt){
  std::uint_least32_t seed_data[std::mt19937::state_size];
  std::generate_n(seed_data, std::mt19937::state_size, std::ref(LilEntropy));
  std::seed_seq q(std::begin(seed_data), std::end(seed_data));
  mt.seed(q);
}

int main(){
  std::mt19937 mt;
  SeedGenerator(mt);

  for(int i=0;i<100;i++)
    std::cout<<mt()<<std::endl;
}

여기서 아이디어는 XOR을 사용하여 엔트로피의 많은 잠재적 소스 (빠른 시간, 느린 시간 std::random-device, 정적 변수 위치, 힙 위치, 함수 위치, 라이브러리 위치, 프로그램 별 값)를 결합하여 초기화에 최선을 다하는 것입니다. mt19937. 최소한 한 번의 소스가 "좋음"이면 결과는 최소한 "좋음"이됩니다.

이 대답은 바람직한 것만 큼 짧지 않으며 논리 오류가 하나 이상 포함될 수 있습니다. 그래서 나는 그것을 진행중인 작업이라고 생각하고 있습니다. 피드백이 있으면 의견을 남겨주세요.


3
주소는 임의성이 거의 없을 수 있습니다. 항상 동일한 할당이 있으므로 전체 메모리에 액세스 할 수있는 더 작은 임베디드 시스템에서는 매번 동일한 결과를 얻을 수 있습니다. 나는 그것이 큰 시스템에는 충분할 것이라고 말하고 싶지만 마이크로 컨트롤러에서는 엉망일지도 모른다.
meneldal

1
&i ^ &myseed둘 다 동일한 번역 단위에서 정적 저장 기간을 가진 객체이므로 서로 가깝기 때문에 둘 중 하나만 사용하는 것보다 엔트로피가 상당히 적어야한다고 생각 합니다. 그리고 당신은 실제로 초기화의 특수 값을 사용하지 않는 것 같습니다 myseed.
aschepler

7
할당 해제 된 포인터를 int로 변환하는 것은 정의되지 않은 동작입니다. 여전히 존재하는 동안 수행하십시오. ^끔찍한 해시 결합기입니다. 두 값 모두 엔트로피가 많지만 서로 비교하면 거의없는 경우 제거됩니다. +일반적으로 더 좋습니다 (x + x는 x에서 1 비트의 엔트로피 만 연소하고 x ^ x는 모두 연소하므로). 기능 THEAD 안전 I 용의자 (아닙니다 rd())
Yakk - 아담 Nevraumont

2
아 그리고 +나는 서명되지 않은 것을 의미합니다 ( +서명은 UB- 베이트입니다). 이것들은 다소 우스꽝스러운 UB 케이스이지만 당신은 휴대용이라고 말했습니다. 또한 정수 값 가능하면 같은 함수의 주소를 받고 고려 (이 경우 불확실한?)
Yakk - 아담 Nevraumont

1
@meneldal : 최대 성능 PC에서도 할당이 다른 물리적 위치 (프로세스 외부의 시스템 상태에 따라 다름)를 얻을 수 있지만 포인터는 프로세스 가상 주소 공간에 의해 추상화되며 특히 반복 가능성이 높습니다. ASLR이 적용되지 않습니다.
Ben Voigt

0
  • getentropy ()를 사용하여 PRNG (pseudorandom number generator)를 시드합니다.
  • /dev/urandom또는 대신 임의의 값을 원한다면 getrandom ()을 사용하십시오 /dev/random.

Linux, Solaris 및 OpenBSD와 같은 최신 UNIX 계열 시스템에서 사용할 수 있습니다.


-2

주어진 플랫폼은 /dev/random. Epoch with 이후 나노초 std::chrono::high_resolution_clock::now()는 아마도 표준 라이브러리에서 최고의 시드 일 것입니다.

이전에 (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )보안에 중요하지 않은 응용 프로그램에 대해 더 많은 엔트로피를 얻는 것과 같은 것을 사용했습니다 .


2
/dev/urandom특히 이와 같은 경우 에는를 사용해야합니다 . /dev/random([/ dev / random에 의해 생성 된 바이트의 임의성을 추정하는 여러 OS에 대한 자세한 설명 삽입]).
Alexander Huszagh

2
@AlexanderHuszagh 사실, /dev/urandom존재하지 않는 시스템에서 코드를 작성해야 했고 차단의 대안은 결정론이었습니다. 상자에는 /dev/hwrng또는 /dev/hw_random그뿐만 아니라 더 좋을 것입니다.
Davislor

좋아요, 저는 "예를 들어 /dev/random," 라고 말했고 그 예를 들었을 때 의도하지 않았던 Linux /dev/random와의 대결 /dev/urandom에 대한 거룩한 전쟁을 일으킨 것 같습니다 .
Davislor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.