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 satuunsigned 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_device
dapat, 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 daritime(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_device
atautime(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_device
dapat dihaluskan (mungkin melalui XOR) dengantime(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 yangstd::random_device
mungkin dilakukan atau tidak dilakukan.
std::random_device
,,time(NULL)
dan alamat fungsi, kemudian di-XOR bersama-sama untuk menghasilkan semacam sumber entropi dengan upaya terbaik.std::random_device
dengan benar pada platform yang Anda rencanakan untuk menjalankan program Anda, dan menyediakan fungsi pembantu yang membuat generator unggulan (seed11::make_seeded<std::mt19937>()
)Jawaban:
Saya berpendapat bahwa kekurangan terbesar
std::random_device
adalah 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
sysrandom
bawah) untuk menyemai PRNG.Windows
Anda dapat mengandalkan
CryptGenRandom
, CSPRNG. Misalnya, Anda dapat menggunakan kode berikut:Seperti Unix
Pada banyak sistem mirip Unix, Anda harus menggunakan / dev / urandom bila memungkinkan (walaupun ini tidak dijamin ada pada sistem yang mendukung POSIX).
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).
Perbandingan Untuk Meningkatkan
Kita dapat melihat paralel untuk meningkatkan :: random_device (CSPRNG yang sebenarnya) setelah melihat sekilas kode sumber . Boost digunakan
MS_DEF_PROV
di Windows, yang merupakan jenis penyediaPROV_RSA_FULL
. Satu-satunya hal yang hilang adalah memverifikasi konteks kriptografi, yang dapat dilakukan denganCRYPT_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,
getrandom
ini adalah pilihan yang sangat baik di Linux 3.17 ke atas, dan di Solaris terbaru.getrandom
berperilaku identik dengan/dev/urandom
, kecuali itu memblokir jika kernel belum menginisialisasi CSPRNG-nya setelah booting. Cuplikan berikut mendeteksi apakah Linuxgetrandom
tersedia, dan jika tidak kembali ke/dev/urandom
.OpenBSD
Ada satu peringatan terakhir: OpenBSD modern tidak memilikinya
/dev/urandom
. Anda harus menggunakan getentropy sebagai gantinya.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_filebuf
danFILE
berisi buffer internal, yang akan dialokasikan melalui pengalokasi standar (dan karenanya tidak dihapus dari memori).Ini dapat dengan mudah dilakukan dengan mengubah
sysrandom
ke: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
.sumber
/dev/random
akan menjadi pilihan yang lebih baik untuk penyemaian RNG, tetapi tampaknya/dev/urandom
masih dianggap aman secara komputasi bahkan ketika/dev/random
akan memblokir karena entropi yang tersedia rendah, jadiurandom
adalah pilihan yang disarankan untuk semuanya kecuali mungkin bantalan satu kali. Lihat juga unix.stackexchange.com/questions/324209/… . Waspadalah terhadap benih yang dapat diprediksi sejakurandom
awal setelah boot.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/random
itu./dev/urandom
biasanya berfungsi. Diskusi milis Python tentang ini adalah sesuatu yang biasanya saya ikutiDalam 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.
sumber
std::random_device
akan termasuk dalam kategori itu, tetapi ternyata tidak jika beberapa implementasi nyata menggunakan PRNG benih tetap! Itu jauh melampaui argumen einpoklum.Anda dapat menggunakan
std::seed_seq
dan mengisinya hingga setidaknya ukuran status yang dibutuhkan untuk generator menggunakan metode Alexander Huszagh untuk mendapatkan entropi:Jika ada cara yang tepat untuk mengisi atau membuat file SeedSequence dari UniformRandomBitGenerator di perpustakaan standar, menggunakan
std::random_device
seeding dengan benar akan jauh lebih sederhana.sumber
Implementasi yang saya kerjakan memanfaatkan
state_size
propertimt19937
PRNG untuk memutuskan berapa banyak benih yang akan disediakan saat inisialisasi:Saya pikir ada ruang untuk perbaikan karena
std::random_device::result_type
bisa 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:Ini berarti implementasi hanya dapat menghasilkan nilai deterministik jika dicegah untuk menghasilkan nilai non-deterministik dengan beberapa batasan.
The
MinGW
compiler padaWindows
terkenal tidak memberikan non-deterministik nilai dari yangstd::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.sumber
std::random_device
, dan karena itu rentan terhadap masalah yang berasal darinya.std::random_device
? Saya tahu standar memungkinkanPRNG
sebagai mundur tetapi saya merasa itu hanya untuk menutupi diri mereka sendiri karena sulit untuk menuntut bahwa setiap perangkat yang menggunakanC++
memiliki sumber acak non-deterministik. Dan jika tidak, apa yang bisa Anda lakukan?std::random_device
. Saya percaya itulah semangat standar. Jadi saya telah mencari dan hanya dapat menemukanMinGW
yang rusak dalam hal ini. Sepertinya tidak ada yang melaporkan masalah ini dengan hal lain yang saya temukan. Jadi, di perpustakaan saya, saya hanya menandaiMinGW
sebagai tidak didukung. Jika ada masalah yang lebih luas maka saya akan memikirkannya kembali. Saya hanya tidak melihat buktinya sekarang.std::random_device
semua 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.)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):
sumber
1
dan2
dan amati bahwa urutan pelampung yang dihasilkan oleh mereka membutuhkan beberapa saat untuk benar-benar menyimpang).Inilah tusukan saya sendiri pada pertanyaan itu:
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.
sumber
&i ^ &myseed
harus 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 inisialisasimyseed
?^
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()
)+
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?)/dev/urandom
atau/dev/random
).Ini tersedia pada sistem modern mirip UNIX, seperti Linux, Solaris, dan OpenBSD.
sumber
Platform tertentu mungkin memiliki sumber entropi, seperti
/dev/random
. Nano sejak Epoch denganstd::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.sumber
/dev/urandom
, terutama dalam kasus seperti ini./dev/random
blok, 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])./dev/urandom
tidak ada, dan alternatif untuk memblokir adalah determinisme. Sebuah kotak mungkin memiliki/dev/hwrng
atau/dev/hw_random
juga, yang seharusnya lebih baik./dev/random
," dan itu tampaknya telah memicu perang suci tentang/dev/random
versus/dev/urandom
di Linux yang tidak saya maksudkan ketika saya memberikan contoh itu ..