Bagaimana cara menyemai mt19937 PRNG secara ringkas, portabel, dan menyeluruh?

112

Sepertinya saya melihat banyak jawaban di mana seseorang menyarankan penggunaan <random>untuk menghasilkan angka acak, biasanya bersama dengan kode seperti ini:

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

Biasanya ini menggantikan beberapa jenis "kekejian yang tidak suci" seperti:

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

Kita mungkin mengkritik cara lama dengan alasan yang time(NULL)memberikan entropi rendah, time(NULL)dapat diprediksi, dan hasil akhirnya tidak seragam.

Tapi semua itu benar dengan cara baru: hanya memiliki lapisan yang lebih berkilau.

  • rd() mengembalikan satu unsigned int . Ini memiliki setidaknya 16 bit dan mungkin 32. Itu tidak cukup untuk menyemai kondisi 19937 bit MT.

  • Menggunakan std::mt19937 gen(rd());gen()(seeding dengan 32 bit dan melihat keluaran pertama) tidak memberikan distribusi keluaran yang baik. 7 dan 13 tidak pernah bisa menjadi keluaran pertama. Dua biji menghasilkan 0. Dua belas biji menghasilkan 1226181350. ( Link )

  • std::random_devicedapat, dan terkadang, diimplementasikan sebagai PRNG sederhana dengan benih tetap. Oleh karena itu, mungkin menghasilkan urutan yang sama di setiap proses. ( Link ) Ini bahkan lebih buruk dari time(NULL).

Lebih buruk lagi, sangat mudah untuk menyalin dan menempelkan potongan kode sebelumnya, terlepas dari masalah yang dikandungnya. Beberapa solusi untuk ini memerlukan memperoleh largish perpustakaan yang mungkin tidak cocok untuk semua orang.

Sehubungan dengan hal ini, pertanyaan saya adalah Bagaimana cara menyemai mt19937 PRNG secara ringkas, portabel, dan menyeluruh dalam C ++?

Mengingat masalah di atas, jawaban yang bagus:

  • Harus sepenuhnya menyemai mt19937 / mt19937_64.
  • Tidak bisa hanya mengandalkan std::random_deviceatautime(NULL) sebagai sumber entropi.
  • Sebaiknya tidak mengandalkan Boost atau libaries lainnya.
  • Harus muat dalam sejumlah kecil baris sehingga akan terlihat bagus saat disalin ke dalam jawaban.

Pikiran

  • Pemikiran saya saat ini adalah bahwa keluaran dari std::random_devicedapat dihaluskan (mungkin melalui XOR) dengan time(NULL), nilai yang berasal dari pengacakan ruang alamat , dan konstanta hard-code (yang dapat diatur selama distribusi) untuk mendapatkan bidikan upaya terbaik di entropi.

  • std::random_device::entropy() tidak memberikan indikasi yang baik tentang apa yang std::random_devicemungkin dilakukan atau tidak dilakukan.

Richard
sumber
24
@ Fabien: Apa portabel tentang itu? Ini adalah pertanyaan C ++, bukan pertanyaan Linux.
Balapan Ringan di Orbit
6
Pemikiran pribadi saya adalah bahwa mungkin nilai-nilai dapat diambil dari std::random_device,, time(NULL)dan alamat fungsi, kemudian di-XOR bersama-sama untuk menghasilkan semacam sumber entropi dengan upaya terbaik.
Richard
5
Akan lebih baik jika ada fungsi seperti does_random_device_actually_work () sehingga setidaknya seseorang dapat menurunkan dengan baik, atau menghasilkan peringatan atau kesalahan bagi pengguna.
4
Solusi yang tepat tidaklah singkat, solusi singkat tidak akan tepat. Pendekatan saya yang saya gunakan di pustaka seed11 saya pada dasarnya adalah menerapkan std::random_devicedengan benar pada platform yang Anda rencanakan untuk menjalankan program Anda, dan menyediakan fungsi pembantu yang membuat generator unggulan ( seed11::make_seeded<std::mt19937>())
milleniumbug
5
Selain: poin kedua Anda tidak menambahkan sesuatu yang baru. Tidaklah mengherankan jika Anda menemukan beberapa nilai yang muncul 12 kali. Anda seharusnya mengharapkan ada lebih dari tiga nilai yang muncul tepat 12 kali , dengan asumsi Anda memiliki 2 ^ 32 sampel acak seragam yang independen .

Jawaban:

58

Saya berpendapat bahwa kekurangan terbesar std::random_deviceadalah bahwa ia diizinkan untuk melakukan fallback deterministik jika tidak ada CSPRNG yang tersedia. Ini saja merupakan alasan yang baik untuk tidak menggunakan PRNGstd::random_device , karena byte yang dihasilkan mungkin bersifat deterministik. Sayangnya tidak menyediakan API untuk mencari tahu kapan ini terjadi, atau untuk meminta kegagalan alih-alih nomor acak berkualitas rendah.

Artinya, tidak ada solusi yang sepenuhnya portabel : namun, ada pendekatan minimal yang layak. Anda dapat menggunakan pembungkus minimal di sekitar CSPRNG (didefinisikan seperti di sysrandombawah) untuk menyemai PRNG.

Windows


Anda dapat mengandalkan CryptGenRandom, CSPRNG. Misalnya, Anda dapat menggunakan kode berikut:

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

Seperti Unix


Pada banyak sistem mirip Unix, Anda harus menggunakan / dev / urandom bila memungkinkan (walaupun ini tidak dijamin ada pada sistem yang mendukung 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;
}

Lain


Jika CSPRNG tidak tersedia, Anda mungkin memilih untuk mengandalkan std::random_device. Namun, saya akan menghindari ini jika memungkinkan, karena berbagai kompiler (terutama, MinGW) mengimplementasikannya dengan sebagai PRNG (sebenarnya, menghasilkan urutan yang sama setiap saat untuk mengingatkan manusia bahwa itu tidak acak dengan benar).

Penyemaian


Sekarang setelah kami memiliki bagian kami dengan overhead minimal, kami dapat menghasilkan bit entropi acak yang diinginkan untuk menyemai PRNG kami. Contoh ini menggunakan (jelas tidak cukup) 32-bit untuk menyemai PRNG, dan Anda harus meningkatkan nilai ini (yang bergantung pada CSPRNG Anda).

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

Perbandingan Untuk Meningkatkan


Kita dapat melihat paralel untuk meningkatkan :: random_device (CSPRNG yang sebenarnya) setelah melihat sekilas kode sumber . Boost digunakan MS_DEF_PROVdi Windows, yang merupakan jenis penyedia PROV_RSA_FULL. Satu-satunya hal yang hilang adalah memverifikasi konteks kriptografi, yang dapat dilakukan dengan CRYPT_VERIFYCONTEXT. Di * Nix, Boost menggunakan /dev/urandom. IE, solusi ini portabel, teruji dengan baik, dan mudah digunakan.

Spesialisasi Linux


Jika Anda bersedia mengorbankan kesederhanaan demi keamanan, getrandomini adalah pilihan yang sangat baik di Linux 3.17 ke atas, dan di Solaris terbaru. getrandomberperilaku identik dengan /dev/urandom, kecuali itu memblokir jika kernel belum menginisialisasi CSPRNG-nya setelah booting. Cuplikan berikut mendeteksi apakah Linux getrandomtersedia, dan jika tidak kembali ke /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


Ada satu peringatan terakhir: OpenBSD modern tidak memilikinya /dev/urandom. Anda harus menggunakan getentropy sebagai gantinya.

#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

Pikiran Lainnya


Jika Anda memerlukan byte acak yang aman secara kriptografik, Anda mungkin harus mengganti fstream dengan open / read / close POSIX yang tidak disangga. Ini karena keduanya basic_filebufdan FILEberisi buffer internal, yang akan dialokasikan melalui pengalokasi standar (dan karenanya tidak dihapus dari memori).

Ini dapat dengan mudah dilakukan dengan mengubah sysrandom ke:

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

Terima kasih


Terima kasih khusus kepada Ben Voigt karena telah menunjukkannya FILE penggunaan pembacaan buffer, dan oleh karena itu sebaiknya tidak digunakan.

Saya juga ingin berterima kasih kepada Peter Cordes karena menyebutkan getrandom, dan kekurangan OpenBSD /dev/urandom.

Alexander Huszagh
sumber
11
Ini adalah apa yang telah saya lakukan di masa lalu, tetapi, atau setidaknya, pertanyaannya adalah WTF tidak dapatkah penulis perpustakaan untuk platform ini melakukan ini untuk kita? Saya berharap akses file dan utas (misalnya) akan diabstraksi oleh implementasi perpustakaan, jadi mengapa tidak pembuatan nomor acak?
2
OP di sini: Alangkah baiknya jika jawaban ini menunjukkan penyemaian sedikit lebih baik. Sebisa mungkin, saya mengharapkan jawaban yang menghasilkan kode copy-pastable yang melakukan pekerjaan lebih baik daripada contoh sederhana yang saya posting dalam pertanyaan saya tanpa memerlukan banyak interpretasi teknis atau pemikiran dari pihak pembuat kode.
Richard
4
Saya pikir /dev/randomakan menjadi pilihan yang lebih baik untuk penyemaian RNG, tetapi tampaknya /dev/urandommasih dianggap aman secara komputasi bahkan ketika /dev/randomakan memblokir karena entropi yang tersedia rendah, jadi urandomadalah pilihan yang disarankan untuk semuanya kecuali mungkin bantalan satu kali. Lihat juga unix.stackexchange.com/questions/324209/… . Waspadalah terhadap benih yang dapat diprediksi sejak urandomawal setelah boot.
Peter Cordes
2
getrandom(2)Panggilan sistem Linux seperti membuka dan membaca /dev/urandom, kecuali ia akan memblokir jika sumber keacakan kernel belum diinisialisasi. Saya pikir ini menyelamatkan Anda dari masalah keacakan kualitas rendah boot awal tanpa memblokir dalam kasus lain seperti /dev/randomitu.
Peter Cordes
1
@PeterCordes, pastinya, dan itu adalah opsi yang bagus jika tersedia. Namun, ini tidak berfungsi di BSD atau * Nix lain, yang /dev/urandombiasanya berfungsi. Diskusi milis Python tentang ini adalah sesuatu yang biasanya saya ikuti
Alexander Huszagh
22

Dalam arti tertentu, ini tidak bisa dilakukan secara portabel. Artinya, seseorang dapat membayangkan platform deterministik penuh yang valid yang menjalankan C ++ (katakanlah, simulator yang mengukur jam mesin secara deterministik, dan dengan I / O yang "ditentukan") di mana tidak ada sumber keacakan untuk menyemai PRNG.

einpoklum
sumber
1
@kbelder: 1. Siapa bilang pengguna adalah orang? 2. Tidak semua program memiliki interaksi pengguna dan Anda tentu tidak dapat berasumsi bahwa selalu ada pengguna di sekitar ...
einpoklum
8
Saya menghargai tanggapan ini, tetapi juga merasa seolah-olah sebuah program harus melakukan upaya terbaik yang masuk akal.
Richard
3
@Richard Setuju, tetapi masalahnya adalah penulis standar C ++ harus (atau setidaknya mencoba paling keras mereka untuk) mengakomodasi situasi aneh semacam ini. Itulah mengapa Anda mendapatkan definisi standar plin-plan semacam ini, di mana Anda mungkin mendapatkan hasil yang layak, tetapi kompilernya masih bisa memenuhi standar meskipun ia mengembalikan sesuatu yang secara fungsional tidak berharga. - Jadi pembatasan Anda ("singkat dan tidak dapat bergantung pada pustaka lain") mengesampingkan respons apa pun, karena Anda secara efektif memerlukan casing khusus platform-oleh-platform / compiler-by-compiler. (misalnya apa yang dilakukan Boost dengan sangat baik.)
RM
2
@Richard menjelaskan, meskipun, adalah bahwa Anda mendapatkan apa yang Anda dapatkan dalam standar karena tidak ada cara portabel untuk menjadi lebih baik. Jika Anda ingin melakukan yang lebih baik (yang merupakan tujuan mulia) Anda harus menerima sejumlah kekejian yang lebih besar atau lebih kecil :)
hobbs
1
@Richard: Terkadang Anda hanya perlu menerima bahwa mungkin saja membuat implementasi C ++ yang memenuhi standar yang tidak berguna. Karena implementasi orang menggunakan untuk apa pun yang penting yang dirancang untuk menjadi berguna, Anda kadang-kadang harus hidup dengan argumen seperti "setiap pelaksanaan waras akan melakukan sesuatu yang masuk akal". Saya berharap itu std::random_deviceakan termasuk dalam kategori itu, tetapi ternyata tidak jika beberapa implementasi nyata menggunakan PRNG benih tetap! Itu jauh melampaui argumen einpoklum.
Peter Cordes
14

Anda dapat menggunakan std::seed_seqdan mengisinya hingga setidaknya ukuran status yang dibutuhkan untuk generator menggunakan metode Alexander Huszagh untuk mendapatkan entropi:

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

Jika ada cara yang tepat untuk mengisi atau membuat file SeedSequence dari UniformRandomBitGenerator di perpustakaan standar, menggunakan std::random_deviceseeding dengan benar akan jauh lebih sederhana.

ratchet freak
sumber
Tidak ada apa pun dalam standar C ++ atau yang menjamin bahwa pembuat nomor acak akan menggunakan seluruh larik saat Anda melakukan seed dari seed_seq. Metode ini akan menyebabkan kegagalan jika Anda menggunakan rng untuk simulasi ilmiah, dan jelas juga untuk kriptografi. Tentang satu-satunya kasus penggunaan untuk ini adalah mengacak videogame, tetapi itu akan menjadi berlebihan.
Kostas
5

Implementasi yang saya kerjakan memanfaatkan state_sizeproperti mt19937PRNG untuk memutuskan berapa banyak benih yang akan disediakan saat inisialisasi:

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

Saya pikir ada ruang untuk perbaikan karena std::random_device::result_typebisa berbeda daristd::mt19937::result_type dalam ukuran dan jangkauan sehingga harus benar-benar diperhitungkan.

Catatan tentang std :: random_device .

Menurut C++11(/14/17)standar:

26.5.6 Kelas random_device [ rand.device ]

2 Jika batasan implementasi mencegah pembuatan bilangan acak non-deterministik, implementasi dapat menggunakan mesin bilangan acak.

Ini berarti implementasi hanya dapat menghasilkan nilai deterministik jika dicegah untuk menghasilkan nilai non-deterministik dengan beberapa batasan.

The MinGWcompiler pada Windowsterkenal tidak memberikan non-deterministik nilai dari yang std::random_device, meskipun mereka menjadi mudah tersedia dari Sistem Operasi. Jadi saya menganggap ini bug dan sepertinya bukan kejadian umum di seluruh implementasi dan platform.

Galik
sumber
1
Ini mungkin memenuhi status MT, tetapi masih bergantung sepenuhnya std::random_device, dan karena itu rentan terhadap masalah yang berasal darinya.
Richard
1
Saya rasa saya menyatakannya dengan cukup jelas dalam pertanyaan. Senang untuk mengklarifikasi / berdiskusi.
Richard
2
@Richard Apakah ada sistem nyata yang tidak benar-benar menerapkan yang masuk akal std::random_device? Saya tahu standar memungkinkan PRNGsebagai mundur tetapi saya merasa itu hanya untuk menutupi diri mereka sendiri karena sulit untuk menuntut bahwa setiap perangkat yang menggunakan C++memiliki sumber acak non-deterministik. Dan jika tidak, apa yang bisa Anda lakukan?
Galik
5
@AlexanderHuszagh Saya tidak begitu yakin. Tujuan saya adalah membuat "solusi portabel" saya bergantung pada perangkat karena jika perangkat mendukung generator non-deterministik maka seharusnya begitu std::random_device. Saya percaya itulah semangat standar. Jadi saya telah mencari dan hanya dapat menemukan MinGWyang rusak dalam hal ini. Sepertinya tidak ada yang melaporkan masalah ini dengan hal lain yang saya temukan. Jadi, di perpustakaan saya, saya hanya menandai MinGWsebagai tidak didukung. Jika ada masalah yang lebih luas maka saya akan memikirkannya kembali. Saya hanya tidak melihat buktinya sekarang.
Galik
5
Saya benar-benar kecewa karena MinGW merusak std::random_devicesemua orang dengan membuatnya tersedia dalam bentuk yang tidak memberikan kemampuan keacakan platform. Implementasi berkualitas rendah mengalahkan tujuan API yang ada. Akan lebih baik IMO jika mereka tidak menerapkannya sama sekali sampai mereka berhasil. (Atau lebih baik, jika API menyediakan cara untuk meminta kegagalan jika kualitas tinggi keacakan tidak tersedia, sehingga MinGW dapat menghindari menyebabkan risiko keamanan sambil tetap memberikan benih yang berbeda untuk game atau apa pun.)
Peter Cordes
2

Tidak ada yang salah dengan penyemaian dengan menggunakan waktu, dengan asumsi Anda tidak membutuhkannya agar aman (dan Anda tidak mengatakan ini perlu). Wawasannya adalah Anda dapat menggunakan hashing untuk memperbaiki non-keacakan. Saya telah menemukan ini berfungsi secara memadai dalam semua kasus, termasuk dan khususnya untuk simulasi Monte Carlo yang berat.

Salah satu fitur bagus dari pendekatan ini adalah ia menggeneralisasi inisialisasi dari kumpulan benih lain yang tidak benar-benar acak. Misalnya, jika Anda ingin setiap utas memiliki RNG sendiri (untuk keamanan utas), Anda dapat menginisialisasi berdasarkan ID utas berciri.

Berikut ini adalah SSCCE , disaring dari basis kode saya (untuk kesederhanaan; beberapa struktur pendukung OO dihilangkan):

#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);
}
imallett.dll
sumber
1
Saya setuju dengan poin Anda bahwa menyemai dengan waktu mungkin cukup baik dalam praktiknya, jika Anda tidak membutuhkannya agar aman. Tapi saya tidak setuju dengan sisa jawaban Anda. Menyemai dengan hash waktu tidak lebih baik daripada menabur dengan waktu itu sendiri.
DW
@DW Secara empiris, jauh lebih baik. Alasannya adalah bahwa hash tersebut terputus-putus dan mencakup rentang nilai yang jauh lebih luas (coba ini sendiri: benih dengan 1dan 2dan amati bahwa urutan pelampung yang dihasilkan oleh mereka membutuhkan beberapa saat untuk benar-benar menyimpang).
imallett
Saya tidak mengerti mengapa itu penting. Kami hanya menjalankan satu benih dalam satu waktu. Ruang nilai yang mungkin untuk benih (entropi benih) adalah sama - hashing tidak meningkatkan entropi. Mungkin Anda bisa mengedit pertanyaan untuk menjelaskan mengapa hashing lebih baik?
DW
0

Inilah tusukan saya sendiri pada pertanyaan itu:

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

Idenya di sini adalah menggunakan XOR untuk menggabungkan banyak sumber entropi potensial (waktu cepat, waktu lambat, std::random-device lokasi variabel statis, lokasi heap, lokasi fungsi, lokasi perpustakaan, nilai khusus program) untuk melakukan upaya terbaik dalam menginisialisasi mt19937. Selama setidaknya satu sumber "baik", hasilnya setidaknya akan "baik".

Jawaban ini tidak sesingkat yang diharapkan dan mungkin mengandung satu atau lebih kesalahan logika. Jadi saya menganggapnya sedang dalam proses. Tolong beri komentar jika Anda memiliki umpan balik.

Richard
sumber
3
Alamatnya mungkin memiliki sedikit keacakan. Anda selalu memiliki alokasi yang sama, jadi pada sistem tertanam yang lebih kecil di mana Anda memiliki akses ke seluruh memori, kemungkinan akan mendapatkan hasil yang sama setiap saat. Saya akan mengatakan itu mungkin cukup baik untuk sistem besar, tetapi mungkin tidak berguna pada mikrokontroler.
meneldal
1
Saya kira &i ^ &myseedharus memiliki entropi yang jauh lebih sedikit daripada salah satunya saja, karena keduanya adalah objek dengan durasi penyimpanan statis dalam unit terjemahan yang sama dan oleh karena itu cenderung agak berdekatan. Dan Anda tampaknya tidak benar-benar menggunakan nilai khusus dari inisialisasi myseed?
aschepler
7
Mengubah petunjuk yang ditangani menjadi int adalah perilaku yang tidak terdefinisi; lakukan selagi masih ada. ^adalah penggabung hash yang mengerikan; jika dua nilai memiliki banyak entropi, tetapi sedikit dibandingkan satu sama lain, ini akan menghapusnya. +biasanya lebih baik (karena x + x hanya membakar 1 bit entropi di x, sedangkan x ^ x membakar semuanya). Fungsi tidak aman Saya curiga ( rd())
Yakk - Adam Nevraumont
2
Oh dan +maksud saya di unsigned ( +di tanda tangani adalah UB-bait). Meskipun ini adalah kasus UB yang agak konyol, Anda memang mengatakan portabel. Juga pertimbangkan untuk mendapatkan alamat fungsi sebagai nilai integral jika memungkinkan (tidak pasti apakah itu?)
Yakk - Adam Nevraumont
1
@meneldal: Bahkan pada PC berkekuatan penuh, meskipun alokasinya mungkin mendapatkan lokasi fisik yang berbeda (bergantung pada status mesin di luar proses), penunjuk diabstraksi oleh ruang alamat virtual proses, dan kemungkinan besar dapat diulang, terutama ASLR tidak berlaku.
Ben Voigt
0
  • Gunakan getentropy () untuk melakukan seed generator nomor pseudorandom (PRNG).
  • Gunakan getrandom () jika Anda menginginkan nilai acak (bukan, katakanlah, /dev/urandomatau /dev/random).

Ini tersedia pada sistem modern mirip UNIX, seperti Linux, Solaris, dan OpenBSD.

Dan Anderson
sumber
-2

Platform tertentu mungkin memiliki sumber entropi, seperti /dev/random. Nano sejak Epoch dengan std::chrono::high_resolution_clock::now()mungkin adalah benih terbaik di Perpustakaan Standar.

Saya sebelumnya telah menggunakan sesuatu seperti (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )untuk mendapatkan lebih banyak bit entropi untuk aplikasi yang tidak penting bagi keamanan.

Davislor
sumber
2
Anda benar-benar harus menggunakan /dev/urandom, terutama dalam kasus seperti ini. /dev/randomblok, dan seringkali tanpa alasan yang baik untuk melakukannya ([masukkan penjelasan panjang tentang berapa banyak OS yang berbeda memperkirakan keacakan byte yang dihasilkan oleh / dev / random]).
Alexander Huszagh
2
@AlexanderHuszagh Benar, meskipun saya harus membuat kode pada sistem yang /dev/urandomtidak ada, dan alternatif untuk memblokir adalah determinisme. Sebuah kotak mungkin memiliki /dev/hwrngatau /dev/hw_randomjuga, yang seharusnya lebih baik.
Davislor
Oke, saya berkata, "seperti /dev/random," dan itu tampaknya telah memicu perang suci tentang /dev/randomversus /dev/urandomdi Linux yang tidak saya maksudkan ketika saya memberikan contoh itu ..
Davislor