Mt19937 PRNG'yi özlü, taşınabilir ve kapsamlı bir şekilde nasıl tohumlayabilirim?


113

Birisinin <random>rastgele sayılar oluşturmak için kullanılmasını önerdiği birçok yanıt görüyorum , genellikle bunun gibi kodlarla birlikte:

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

Genellikle bu, aşağıdaki gibi bir tür "kutsal olmayan iğrençliğin" yerini alır:

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

Biz olabilir eleştirmek savunarak eski yol time(NULL), düşük entropi sağlar time(NULL)tahmin edilebilir ve sonuçta düzgün olmayan olduğunu.

Ancak bunların hepsi yeni yöntem için geçerli: sadece daha parlak bir cilaya sahip.

  • rd()bir single döndürür unsigned int. Bu, en az 16 bit ve muhtemelen 32 bit içerir. Bu, MT'nin 19937 bitlik durumunun tohumlanması için yeterli değildir.

  • Kullanmak std::mt19937 gen(rd());gen()(32 bit ile tohumlama ve ilk çıktıya bakmak) iyi bir çıktı dağılımı sağlamaz. 7 ve 13 asla ilk çıktı olamaz. İki tohum 0 üretir. On iki tohum 1226181350 üretir. ( Link )

  • std::random_devicesabit bir tohumla basit bir PRNG olarak uygulanabilir ve bazen uygulanabilir. Bu nedenle, her çalışmada aynı diziyi üretebilir. ( Link ) Bu daha da kötü time(NULL).

Daha da kötüsü, içerdikleri sorunlara rağmen yukarıdaki kod parçacıklarını kopyalayıp yapıştırmak çok kolaydır. Buna bazı çözümler elde gerektiren büyücek kütüphaneleri herkese uygun olmayabilir.

Bunun ışığında sorum şu: mt19937 PRNG'yi C ++ 'da özlü, taşınabilir ve kapsamlı bir şekilde nasıl tohumlayabilirim?

Yukarıdaki sorunlar göz önüne alındığında, iyi bir cevap:

  • Mt19937 / mt19937_64 tohumlarını tam olarak yerleştirmelidir.
  • Dayanmaz Can std::random_deviceveya time(NULL)entropi kaynağı olarak.
  • Boost ya da diğer hainlere güvenmemelidir.
  • Bir yanıta kopyalayıp yapıştırıldığında güzel görünecek şekilde az sayıda satıra sığmalıdır.

Düşünceler

  • Şu anki düşüncem, entropide en iyi çabayı elde etmek için çıktıların adres alanı rasgele seçilmesinden türetilen değerler ve (dağıtım sırasında ayarlanabilen) sabit kodlu bir sabitle ( std::random_devicebelki de XOR aracılığıyla) karıştırılabileceğidir .time(NULL)

  • std::random_device::entropy() gelmez ne olduğunun iyi bir göstergesidir std::random_deviceya do olabilir veya olmayabilir.


24
@Fabien: Bunda taşınabilir ne var? Bu bir C ++ sorusudur, Linux sorusu değil.
Orbit'te Hafiflik Yarışları

6
Benim kişisel düşüncem, belki değerlerin bir tür en iyi entropi kaynağı üretmek için std::random_device, değerlerden çıkarılabileceği time(NULL)ve adresleri işleyebileceğiydi.
Richard

5
Does_random_device_actually_work () gibi bir işlev olması güzel olurdu, böylece biri en azından zarif bir şekilde indirgeyebilir veya kullanıcı için uyarılar veya hatalar üretebilir.

4
Doğru çözüm kısa değil, kısa çözüm uygun olmayacak. Benim seed11 kitaplığımda kullandığım yaklaşımım temelde std::random_deviceprogramınızı çalıştırmayı planladığınız platformlarda düzgün bir şekilde uygulamak ve seeded generator ( seed11::make_seeded<std::mt19937>()) oluşturan bir yardımcı işlev sağlamaktır
milleniumbug

5
Bir kenara: ikinci merminiz yeni bir şey eklemiyor. 12 kez görünen bir değer bulmanız şaşırtıcı değildir. 2 ^ 32 bağımsız, tekdüze rasgele örneğiniz olduğunu varsayarak, tam olarak 12 kez görünen üç değerden biraz fazla olmasını beklemelisiniz .

Yanıtlar:


58

En büyük kusurun, std::random_deviceCSPRNG mevcut olmadığında deterministik bir geri dönüşe izin verilmesi olduğunu iddia ediyorum . std::random_deviceÜretilen baytlar deterministik olabileceğinden, bu tek başına bir PRNG'yi tohumlamamak için iyi bir nedendir . Ne yazık ki, bunun ne zaman olduğunu bulmak veya düşük kaliteli rastgele sayılar yerine hata istemek için bir API sağlamaz.

Yani, tamamen taşınabilir bir çözüm yoktur : ancak, makul, asgari bir yaklaşım vardır. PRNG'yi tohumlamak için bir CSPRNG'nin ( sysrandomaşağıda tanımlandığı gibi ) etrafında minimal bir sarmalayıcı kullanabilirsiniz .

pencereler


CryptGenRandomBir CSPRNG'ye güvenebilirsiniz . Örneğin, aşağıdaki kodu kullanabilirsiniz:

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;
}

Unix gibi


Pek çok Unix benzeri sistemde, mümkün olduğunda / dev / urandom kullanmalısınız (bunun POSIX uyumlu sistemlerde varlığı garanti edilmese de).

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;
}

Diğer


CSPRNG yoksa, güvenmeyi seçebilirsiniz std::random_device. Bununla birlikte, mümkünse bundan kaçınırım, çünkü çeşitli derleyiciler (en önemlisi, MinGW) bunu bir PRNG olarak uygular (aslında, her seferinde insanları rastgele olmadığı konusunda uyarmak için aynı diziyi üretir).

Tohumlama


Artık minimum ek yükü olan parçalarımıza sahip olduğumuza göre, PRNG'mizi tohumlamak için istenen rastgele entropi bitlerini üretebiliriz. Örnek, PRNG'yi başlatmak için (açıkça yetersiz) 32 bit kullanır ve bu değeri artırmanız gerekir (bu, CSPRNG'nize bağlıdır).

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

Artırmak İçin Karşılaştırma


Kaynak koduna hızlıca baktıktan sonra :: random_device (gerçek bir CSPRNG) geliştirmek için paralellikler görebiliriz . Boost MS_DEF_PROV, için sağlayıcı türü olan Windows'ta kullanır PROV_RSA_FULL. Eksik olan tek şey, kriptografik bağlamın doğrulanması olacaktır ki bu da yapılabilir CRYPT_VERIFYCONTEXT. * Nix'te, Boost kullanır /dev/urandom. IE, bu çözüm taşınabilir, iyi test edilmiş ve kullanımı kolaydır.

Linux Uzmanlığı


Güvenlik için özünden ödün vermeye istekliysen getrandom, Linux 3.17 ve üzeri ve son Solaris'te mükemmel bir seçimdir. Çekirdek, önyüklemeden sonra CSPRNG'sini henüz başlatmamışsa engellemesi dışında, ile getrandomaynı şekilde davranır /dev/urandom. Aşağıdaki kod parçası, Linux'un getrandomkullanılabilir olup olmadığını ve yoksa geri dönüp dönmediğini tespit eder /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


Son bir uyarı var: modern OpenBSD'de yok /dev/urandom. Bunun yerine getentropy kullanmalısınız.

#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

diğer düşünceler


Kriptografik olarak güvenli rasgele baytlara ihtiyacınız varsa, muhtemelen fstream'i POSIX'in arabelleğe alınmamış açma / okuma / kapatma ile değiştirmelisiniz. Her iki Bunun nedeni, basic_filebufve FILEstandart bir ayırıcı ile ayrılan (ve dolayısıyla bellekten sildi) olacak bir iç tampon içerir.

Bu, aşağıdaki şekilde değiştirilerek kolayca yapılabilir 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;
}

Teşekkürler


Ben Voigt'e özel olarak işaret ettiği için ara belleğe FILEalınmış okumalar kullanır ve bu nedenle kullanılmamalıdır.

Ayrıca bahsettiği getrandomve OpenBSD'nin eksikliğinden dolayı Peter Cordes'e teşekkür ederim /dev/urandom.


11
Geçmişte yaptığım şey buydu, ama en azından bir soru şu: WTF bu platformların kütüphane yazarları bunu bizim için yapamaz mı? Dosya erişiminin ve iş parçacığının (örneğin) kütüphane uygulamaları tarafından soyutlanmasını bekliyorum, öyleyse neden rastgele sayı üretimi olmasın?

2
Burada OP: Bu cevabın tohumlamayı biraz daha iyi göstermesi güzel olurdu. Mümkün olduğunca, kodlayıcı tarafında çok fazla teknik yorum veya düşünce gerektirmeden sorumda gönderdiğim basit örnekten daha iyi iş yapan kopyalanabilir kod üreten cevaplar umuyorum.
Richard

4
/dev/randomBir RNG'yi yerleştirmek için daha iyi bir seçim olacağını düşündüm , ancak görünüşe /dev/urandomgöre hala/dev/random düşük entropi nedeniyle bloke olsa bile hesaplama açısından güvenli olduğu düşünülüyor , bu yüzden urandombelki bir kerelik pedler dışında her şey için önerilen seçim budur. Ayrıca bkz . Unix.stackexchange.com/questions/324209/… . urandomYine de, önyüklemeden sonra çok erken bir zamanda tahmin edilebilir tohumlara dikkat edin .
Peter Cordes

2
Linux'un getrandom(2)sistem çağrısı açma ve okuma gibidir /dev/urandom, ancak çekirdeğin rastgelelik kaynakları henüz başlatılmadıysa engelleyecektir. Sanırım bu, sizi erken başlatma düşük kaliteli rastgelelik probleminden kurtarıyor, diğer durumlarda olduğu gibi engellemiyor /dev/random.
Peter Cordes

1
@PeterCordes, elbette ve bu, mevcut olduğunda harika bir seçenek. Ancak, /dev/urandomgenellikle üzerinde çalışan BSD veya diğer * Nix'ler üzerinde çalışmaz . Bu konudaki Python posta listesi tartışması genel olarak abone olduğum bir konudur
Alexander Huszagh

22

Bir anlamda bu taşınabilir bir şekilde yapılamaz. Yani, bir PRNG'yi tohumlamak için rastgelelik kaynağının bulunmadığı, C ++ çalıştıran geçerli bir tam-deterministik platform (örneğin, makine saatini belirleyici olarak ve "belirlenmiş" I / O ile adımlayan bir simülatör) tasarlayabilir.


1
@kbelder: 1. Kullanıcının bir kişi olduğunu kim söylüyor? 2. Tüm programların kullanıcı etkileşimi yoktur ve etrafta her zaman bir kullanıcının olduğunu kesinlikle
varsayamazsınız

8
Bu yanıtı takdir ediyorum, ancak aynı zamanda bir programın makul bir çaba göstermesi gerektiğini düşünüyorum.
Richard

3
@Richard Kabul Edildi, ancak sorun şu ki, C ++ standart yazarları bu tür tuhaf durumlara uyum sağlamak zorunda (veya en azından ellerinden gelenin en iyisini yapmaya çalışmak). İşte bu yüzden, iyi sonuçlar alabileceğiniz bu türden saçma sapan standart tanımları elde edersiniz, ancak derleyici işlevsel olarak değersiz bir şeyi geri verse bile yine de standartlara uyumlu olabilir. - Dolayısıyla, kısıtlamalarınız ("kısadır ve diğer kitaplıklara güvenemez") herhangi bir yanıtı ortadan kaldırır, çünkü etkili bir şekilde platforma / platforma / derleyiciye göre özel bir kasaya ihtiyaç duyarsınız. (örneğin, Boost'un bu kadar iyi yaptığı şey.)
RM

2
@Richard'ın açıkladığı şey, standartta aldığınız şeyi elde etmenizdir, çünkü daha iyisini yapmanın taşınabilir bir yolu yoktur. Daha iyisini yapmak istiyorsan (ki bu asil bir hedeftir) az ya da çok iğrençliği kabul etmelisin :)
hobbs

1
@Richard: Bazen, kullanışlı olmayan standartlarla uyumlu bir C ++ uygulaması yapmanın mümkün olduğunu kabul etmeniz gerekir. İnsanların önemli olan herhangi bir şey için kullandıkları uygulamalar faydalı olacak şekilde tasarlandığından, bazen "herhangi bir mantıklı uygulama mantıklı bir şey yapar" gibi argümanlarla yaşamak zorunda kalırsınız. Bunun std::random_devicebu kategoride olmasını umardım, ancak görünüşe göre bazı gerçek uygulamalar sabit çekirdekli bir PRNG kullanıyorsa değil! Bu, einpoklum'un argümanının çok ötesine geçer.
Peter Cordes

14

std::seed_seqAlexander Huszagh'ın entropi elde etme yöntemini kullanarak a kullanabilir ve en azından jeneratör için gerekli durum boyutuna kadar doldurabilirsiniz:

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);
}

Standart kitaplıkta bir UniformRandomBitGenerator'dan bir SeedSequence'i doldurmanın veya oluşturmanın uygun bir yolu olsaydı, düzgün bir şekilde tohumlama için kullanmak çok daha kolay olurdu.std::random_device



C ++ standardında ya da herhangi bir şey, seed_seq'ten tohumladığınızda rastgele sayı üretecinin tüm diziyi kullanacağını garanti edecek hiçbir şey yoktur. Rng'yi bilimsel bir simülasyon ve tabii ki kriptografi için kullanıyorsanız, bu yöntem başarısızlığa yol açacaktır. Bunun için tek kullanım durumu, bir video oyununu rastgele hale getirmek olacaktır, ancak orada aşırı bir şey olacaktır.
Kostas

5

Üzerinde çalıştığım uygulama , başlatma sırasında kaç tohum sağlayacağına karar vermek state_sizeiçin mt19937PRNG'nin özelliğinden yararlanıyor :

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});
}

Bence iyileştirme için yer var çünkü boyut ve aralık std::random_device::result_typeaçısından farklı olabilir, std::mt19937::result_typebu yüzden gerçekten dikkate alınmalıdır.

Std :: random_device hakkında bir not .

Göre C++11(/14/17)standart (ler):

26.5.6 Sınıf random_device [ rand.device ]

2 Uygulama sınırlamaları deterministik olmayan rasgele sayılar üretmeyi engelliyorsa, uygulama bir rasgele sayı motoru kullanabilir.

Bu, uygulamanın yalnızca belirli sınırlamalarla deterministik olmayan değerler üretmesi engellendiğinde deterministik değerler üretebileceği anlamına gelir .

MinGWÜzerinde derleyici Windowsünlü sağlamaz olmayan deterministik onun değerleri std::random_deviceonları İşletim Sistemi kolayca kullanılabilir olmasına rağmen. Bu yüzden bunu bir hata olarak görüyorum ve muhtemelen uygulamalar ve platformlar arasında yaygın bir olay değil.


1
Bu MT durumunu doldurabilir, ancak yine de yalnızca dayanır std::random_deviceve bu nedenle ondan kaynaklanan sorunlara karşı savunmasızdır.
Richard

1
Sanırım soruda yeterince net ifade ettim. Yine de açıklığa kavuşturmaktan / tartışmaktan mutluluk duyarım.
Richard

2
@Richard Makul bir uygulamayı gerçekten uygulamayan gerçek sistemler var std::random_devicemı? Standardın PRNGgeri dönüşe izin verdiğini biliyorum, ancak bunun sadece kendilerini örtmek için olduğunu hissediyorum, çünkü kullanan her cihazın C++deterministik olmayan rastgele bir kaynağa sahip olmasını talep etmek zor . Ve yapmazlarsa, bununla ilgili ne yapabilirsin?
Galik

5
@AlexanderHuszagh O kadar emin değilim. Niyetim, "taşınabilir çözümümü" aygıta bağımlı kılmaktır , çünkü aygıt deterministik olmayan üreteçleri destekliyorsa, o zaman olmalıdır std::random_device. Bunun standardın ruhu olduğuna inanıyorum. Bu yüzden araştırdım ve sadece MinGWbu açıdan kırılmış olanı bulabilirim . Hiç kimse bulduğum başka bir şeyle bu sorunu bildirmiyor gibi görünüyor. Bu yüzden, kütüphanemde, sadece MinGWdesteklenmiyor olarak işaretledim . Daha geniş bir sorun olsaydı, yeniden düşünürdüm. Şu anda bunun kanıtını göremiyorum.
Galik

5
MinGW'nin std::random_deviceplatformun rastgelelik yeteneklerini sunmayan bir biçimde sunarak herkesi mahvettiği için gerçekten hayal kırıklığına uğradım . Düşük kaliteli uygulamalar, mevcut API'nin amacını bozar. Çalışana kadar onu hiç uygulamasalar daha iyi IMO olurdu. (API isteği yetmezliği için bir yol verdiyse MinGW hala oyunlarda ya da her neyse için farklı tohum verirken güvenlik riskleri neden önlemek böylece Hatta, yüksek kaliteli rasgelelik, mevcut olmasaydı.)
Peter Cordes

2

Güvende olmak için ihtiyacınız olmadığını varsayarak zamanı kullanarak tohumlamada yanlış bir şey yoktur (ve bunun gerekli olduğunu söylemediniz). Buradaki fikir, rastgele olmayışı düzeltmek için hashing kullanabileceğinizdir. Bunun, özellikle ağır Monte Carlo simülasyonları dahil olmak üzere her durumda yeterince işe yaradığını buldum.

Bu yaklaşımın güzel bir özelliği, diğer gerçekten rastgele olmayan tohum setlerinden başlatmaya genellemesidir. Örneğin, her bir iş parçacığının kendi RNG'sine sahip olmasını istiyorsanız (iş parçacığı güvenliği için), karma iş parçacığı kimliğine göre başlatabilirsiniz.

Aşağıdakiler, kod tabanımdan damıtılmış bir SSCCE'dir (basitlik için; bazı OO destek yapıları çıkarılır):

#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
Güvende olmak için ihtiyacınız yoksa, zamanla tohumlamanın pratikte muhtemelen yeterince iyi olduğu fikrine katılıyorum. Ama cevabınızın geri kalanına katılamıyorum. Zamanın karması ile tohumlamak, zamanın kendisiyle tohumlamaktan daha iyi değildir.
DW

@DW Ampirik olarak çok daha iyi. Bunun nedeni, hash'in süreksiz olması ve çok daha geniş bir değer aralığını kapsamasıdır (bunu kendiniz deneyin: tohumlayın 1ve 2onlar tarafından üretilen kayan nokta dizisinin gerçekten farklılaşmasının biraz zaman aldığını gözlemleyin).
imallett

Bunun neden önemli olduğunu anlamıyorum. Bir seferde sadece tek bir tohumla koşuyoruz. Tohum için olası değerlerin alanı (tohumun entropisi) her iki şekilde de aynıdır - hashing entropiyi artırmaz. Hashing'in neden daha iyi olduğunu açıklamak için soruyu düzenleyebilirsiniz.
DW

0

İşte soruya kendi bıçağım:

#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;
}

Buradaki fikir, XOR'u, birçok potansiyel entropi kaynağını (hızlı zaman, yavaş zaman std::random-device, statik değişken konumları, yığın konumları, işlev konumları, kitaplık konumları, programa özgü değerler) birleştirmek için kullanmaktır. mt19937. Kaynak en az bir kez "iyi" olduğu sürece, sonuç en azından o kadar "iyi" olacaktır.

Bu cevap, tercih edilebilecek kadar kısa değildir ve bir veya daha fazla mantık hatası içerebilir. Bu yüzden devam eden bir çalışma olduğunu düşünüyorum. Geri bildiriminiz varsa lütfen yorum yapın.


3
Adreslerin çok az rastgeleliği olabilir. Her zaman aynı tahsislere sahip olursunuz, bu nedenle tüm belleğe eriştiğiniz daha küçük gömülü sistemlerde, her seferinde aynı sonuçları alma olasılığı yüksektir. Büyük bir sistem için muhtemelen yeterince iyi olduğunu söyleyebilirim, ancak bir mikro denetleyiciye zarar verebilir.
meneldal

1
Her &i ^ &myseedikisi de aynı çeviri biriminde statik depolama süresine sahip nesneler olduğundan ve bu nedenle büyük olasılıkla birbirine oldukça yakın olduğundan, tek başına ikisinden de önemli ölçüde daha az entropiye sahip olması gerektiğini tahmin ediyorum . Ve gerçekten ilklendirilmesindeki özel değeri kullanmıyor gibi görünüyorsunuz myseed?
aschepler

7
Dağıtık işaretçileri tamsalara dönüştürmek tanımsız bir davranıştır; hala varken yapın. ^korkunç bir hash birleştiricidir; iki değerin her ikisinde de çok fazla entropi varsa, ancak birbirine kıyasla çok azsa, onu kaldırır. +genellikle daha iyidir (çünkü x + x, x'te yalnızca 1 bit entropi yakarken, x ^ x hepsini yakar). İşlev şüphelendiğim güvende değil ( rd())
Yakk - Adam Nevraumont

2
Oh ve +derken işaretsiz demek istiyorum ( +imzalandığında UB-yem). Bunlar biraz saçma UB davaları olsa da, taşınabilir dediniz. Ayrıca mümkünse bir fonksiyonun adresini bir integral değer olarak almayı düşünün (olup olmadığı belirsiz mi?)
Yakk - Adam Nevraumont

1
@meneldal: Tam güçlü bir bilgisayarda bile, ayırmalar farklı fiziksel konumlar alabilse de (işlemin dışındaki makinenin durumuna bağlı olarak), işaretçiler işlem sanal adres alanı tarafından soyutlanır ve muhtemelen oldukça tekrarlanabilir, özellikle ASLR yürürlükte değil.
Ben Voigt

0
  • Bir sözde rasgele sayı üretecini (PRNG) başlatmak için getentropy () kullanın.
  • Rastgele değerler istiyorsanız (örneğin /dev/urandomveya yerine) getrandom () kullanın /dev/random.

Bunlar Linux, Solaris ve OpenBSD gibi modern UNIX benzeri sistemlerde mevcuttur.


-2

Belirli bir platformda, gibi bir entropi kaynağı olabilir /dev/random. Epoch'tan beri geçen nanosaniyeler std::chrono::high_resolution_clock::now(), muhtemelen Standart Kitaplıktaki en iyi tohumdur.

Daha önce, (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )güvenlik açısından kritik olmayan uygulamalar için daha fazla entropi biti elde etmek gibi bir şey kullanmıştım .


2
/dev/urandomÖzellikle böyle bir durumda gerçekten kullanmalısınız . /dev/randombloklar ve genellikle bunu yapmak için iyi nedenler olmadan ([/ dev / random tarafından üretilen baytların rasgeleliğini kaç farklı işletim sisteminin tahmin ettiği hakkında uzun açıklama ekleyin]).
Alexander Huszagh

2
@AlexanderHuszagh Doğru, /dev/urandomvar olmayan sistemler üzerinde kodlama yapmak zorunda olmama rağmen engellemenin alternatifi determinizmdi. Bir kutuda olabilir /dev/hwrngya /dev/hw_randomda olabilir , ki bu daha da iyi olmalıdır.
Davislor

Tamam, "gibi" dedim /dev/randomve /dev/randombu /dev/urandom, Linux'a karşı kutsal bir savaşı ateşlemiş gibi görünüyor ki bu örneği verirken niyet etmedim ..
Davislor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.