Kode di bawah ini dimaksudkan untuk menghasilkan daftar lima nomor pseudo-random dalam interval [1.100]. Saya menyemai default_random_engine
dengan time(0)
, yang mengembalikan waktu sistem dalam waktu unix . Ketika saya mengkompilasi dan menjalankan program ini di Windows 7 menggunakan Microsoft Visual Studio 2013, ini berfungsi seperti yang diharapkan (lihat di bawah). Ketika saya melakukannya di Arch Linux dengan kompiler g ++, bagaimanapun, itu berperilaku aneh.
Di Linux, 5 angka akan dihasilkan setiap kali. 4 angka terakhir akan berbeda pada setiap eksekusi (seperti yang sering terjadi), tetapi angka pertama akan tetap sama.
Contoh keluaran dari 5 eksekusi di Windows dan Linux:
| 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
Menambah misteri, angka pertama itu bertambah satu secara berkala di Linux. Setelah mendapatkan keluaran di atas, saya menunggu sekitar 30 menit dan mencoba lagi untuk menemukan bahwa angka pertama telah berubah dan sekarang selalu dihasilkan sebagai 26. Angka ini terus bertambah 1 secara berkala dan sekarang menjadi 32. Tampaknya sesuai dengan nilai perubahan time(0)
.
Mengapa angka pertama jarang berubah saat berjalan, dan jika berubah, bertambah 1?
Kode. Ini dengan rapi mencetak 5 angka dan waktu sistem:
#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;
}
sizeof(time_t)
vs.sizeof(default_random_engine::result_type)
?default_random_engine
kedua platform tersebut sangat berbeda.Jawaban:
Inilah yang terjadi:
default_random_engine
di libstdc ++ (pustaka standar GCC) adalahminstd_rand0
, yang merupakan mesin kongruensial linier sederhana:typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
Cara mesin ini menghasilkan angka acak adalah x i + 1 = (16807x i + 0) mod 2147483647.
Oleh karena itu, jika benih berbeda dengan 1, maka sebagian besar waktu jumlah pertama yang dihasilkan akan berbeda 16807.
Kisaran generator ini adalah [1, 2147483646]. Cara libstdc ++
uniform_int_distribution
memetakannya menjadi integer dalam kisaran [1, 100] pada dasarnya adalah ini: menghasilkan angkan
. Jika angkanya tidak lebih besar dari 2147483600, maka kembalikan(n - 1) / 21474836 + 1
; jika tidak, coba lagi dengan nomor baru.Seharusnya mudah untuk melihat bahwa dalam sebagian besar kasus, dua
n
s yang hanya berbeda 16807 akan menghasilkan angka yang sama dalam [1, 100] di bawah prosedur ini. Faktanya, orang akan mengharapkan jumlah yang dihasilkan meningkat satu setiap 21474836/16807 = 1278 detik atau 21,3 menit, yang sesuai dengan pengamatan Anda.MSVC ini
default_random_engine
adalahmt19937
, yang tidak memiliki masalah ini.sumber
rand()
% 7 selalu mengembalikan 0rand()
agak bisa dimengerti (itu omong kosong warisan tanpa harapan). Menggunakan PRNG shit-tier untuk sesuatu yang baru tidak bisa dimaafkan. Saya bahkan akan menganggap ini sebagai pelanggaran standar, karena standar tersebut mensyaratkan "sediakan setidaknya perilaku mesin yang dapat diterima untuk penggunaan yang relatif santai, tidak ahli, dan / atau ringan." yang tidak disediakan oleh implementasi ini karena gagal total bahkan untuk kasus penggunaan sepele sepertirand % 7
contoh Anda .rand()
agak bisa dimengerti? Apakah hanya karena tidak ada yang berpikir untuk melakukannya?srand
terlalu kecil untuk menghasilkan benih yang unik dengan mudah. 3) Ini mengembalikan integer dengan implementasi yang ditentukan batas atas yang pemanggil entah bagaimana harus mengurangi ke angka dalam kisaran yang diinginkan, yang bila dilakukan dengan benar lebih banyak pekerjaan daripada menulis pengganti dengan API yang waras untukrand()
4) Menggunakan status global yang bisa berubahThe
std::default_random_engine
adalah implementasi didefinisikan. Gunakanstd::mt19937
ataustd::mt19937_64
sebagai gantinya.Selain itu,
std::time
danctime
fungsinya tidak terlalu akurat, gunakan jenis yang ditentukan di<chrono>
header sebagai gantinya:#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; }
sumber
std::random_device
alih-alih current_time untuk menyemai generator acak Anda. Silakan periksa contoh cppreference tentang Random.ctime
adalah 1 detik. Granularitasstd::chrono
implementasi ditentukan oleh pengguna, default ke, untukstd::high_resolution_clock
(dalam Visual Studio ini adalah typedef untukstd::steady_clock
), nanodetik tetapi dapat memilih pengukuran yang jauh lebih kecil, oleh karena itu, jauh lebih tepat.Di Linux, fungsi acak bukanlah fungsi acak dalam arti probabilistik, tetapi generator bilangan acak semu. Itu diasinkan dengan biji, dan berdasarkan biji itu, jumlah yang dihasilkan acak semu dan tersebar merata. Cara Linux memiliki keuntungan bahwa dalam desain eksperimen tertentu yang menggunakan informasi dari populasi, dapat diukur pengulangan eksperimen dengan tweaker informasi input yang diketahui. Ketika program terakhir siap untuk pengujian kehidupan nyata, garam (benih), dapat dibuat dengan meminta pengguna untuk menggerakkan mouse, mencampur gerakan mouse dengan beberapa penekanan tombol dan menambahkan sejumput hitungan mikrodetik sejak awal kekuatan terakhir.
Benih nomor acak Windows diperoleh dari kumpulan nomor mouse, keyboard, jaringan dan waktu hari. Itu tidak dapat diulang. Tetapi nilai garam ini dapat disetel ulang ke benih yang diketahui, jika seperti yang disebutkan di atas, seseorang terlibat dalam desain percobaan.
Oh ya, Linux memiliki dua generator bilangan acak. Satu, defaultnya adalah modulo 32bits, dan yang lainnya adalah modulo 64bits. Pilihan Anda bergantung pada kebutuhan akurasi dan jumlah waktu komputasi yang ingin Anda gunakan untuk pengujian atau penggunaan sebenarnya.
sumber
collection of mouse, keyboard, network and time of day numbers