Masalah paling mendasar dari aplikasi pengujian Anda adalah bahwa Anda menelepon srand
sekali dan kemudian memanggil rand
satu kali dan keluar.
Inti dari srand
fungsi adalah untuk menginisialisasi urutan bilangan pseudo-acak dengan seed acak.
Ini berarti bahwa jika Anda lulus nilai yang sama untuk srand
dua aplikasi yang berbeda (dengan sama srand
/rand
pelaksanaan) maka Anda akan mendapatkan persis urutan yang sama dari rand()
nilai-nilai membaca setelah itu di kedua aplikasi.
Namun dalam contoh aplikasi Anda urutan pseudo-acak hanya terdiri dari satu elemen - elemen pertama dari urutan pseudo-acak yang dihasilkan dari seed sama dengan waktu second
presisi saat ini. Apa yang Anda harapkan dari output?
Tentunya ketika Anda menjalankan aplikasi pada detik yang sama - Anda menggunakan nilai seed yang sama - dengan demikian hasil Anda sama saja (seperti Martin York sudah disebutkan dalam komentar untuk pertanyaan).
Sebenarnya Anda harus menelepon srand(seed)
satu kali dan kemudian menelepon rand()
berkali-kali dan menganalisis urutan itu - itu akan terlihat acak.
EDIT:
Oh, aku mengerti. Rupanya deskripsi verbal tidak cukup (mungkin hambatan bahasa atau sesuatu ... :)).
BAIK. Contoh kode C kuno berdasarkan pada srand()/rand()/time()
fungsi yang sama yang digunakan dalam pertanyaan:
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
int main(void)
{
unsigned long j;
srand( (unsigned)time(NULL) );
for( j = 0; j < 100500; ++j )
{
int n;
/* skip rand() readings that would make n%6 non-uniformly distributed
(assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
{ /* bad value retrieved so get next one */ }
printf( "%d,\t%d\n", n, n % 6 + 1 );
}
return 0;
}
^^^ ITU berurutan dari menjalankan tunggal dari program ini seharusnya terlihat acak.
EDIT2:
Ketika menggunakan pustaka standar C atau C ++, penting untuk dipahami bahwa sampai sekarang tidak ada fungsi standar tunggal atau kelas yang benar-benar menghasilkan data acak (dijamin oleh standar). Satu-satunya alat standar yang mendekati masalah ini adalah std :: random_device yang sayangnya masih tidak memberikan jaminan keacakan yang sebenarnya.
Tergantung pada sifat aplikasi Anda harus memutuskan terlebih dahulu apakah Anda benar-benar membutuhkan data yang benar-benar acak (tidak dapat diprediksi). Kasus penting ketika Anda paling pasti membutuhkan keacakan benar adalah keamanan informasi - misalnya menghasilkan kunci simetris, kunci pribadi asimetris, nilai garam, token keamanan, dll.
Namun angka acak tingkat keamanan adalah industri terpisah yang bernilai artikel terpisah.
Dalam kebanyakan kasus Pseudo-Random Number Generator sudah cukup - misalnya untuk simulasi ilmiah atau permainan. Dalam beberapa kasus, urutan pseudo-acak yang didefinisikan secara konsisten bahkan diperlukan - misalnya dalam game Anda dapat memilih untuk menghasilkan peta yang sama persis dalam runtime untuk menghindari penyimpanan banyak data.
Pertanyaan asli dan pengulangan banyak pertanyaan yang identik / serupa (dan bahkan banyak "jawaban" yang salah kepada mereka) menunjukkan bahwa pertama dan terutama penting untuk membedakan angka acak dari angka pseudo-acak DAN untuk memahami apa urutan nomor pseudo-acak dalam tempat pertama DAN untuk menyadari bahwa generator nomor pseudo-acak TIDAK digunakan dengan cara yang sama Anda bisa menggunakan generator nomor acak benar.
Secara intuitif ketika Anda meminta nomor acak - hasil yang dikembalikan tidak harus bergantung pada nilai yang dikembalikan sebelumnya dan tidak boleh tergantung jika ada yang meminta sesuatu sebelumnya dan tidak boleh bergantung pada saat apa dan pada proses apa dan pada komputer apa dan dari generator apa dan dalam galaksi apa yang diminta. Lagipula, itulah arti kata "acak" - tidak dapat diprediksi dan terlepas dari apa pun - jika tidak, kata itu tidak acak lagi, bukan? Dengan intuisi ini, adalah wajar untuk mencari di web beberapa mantra sihir untuk mendapatkan angka acak dalam konteks yang memungkinkan.
^^^ ITU semacam ekspektasi intuitif SANGAT SALAH dan berbahaya dalam semua kasus yang melibatkan Pseudo-Random Number Generator - meskipun masuk akal untuk nomor acak yang sebenarnya.
Sementara pengertian "bilangan acak" yang berarti ada - tidak ada yang namanya "bilangan acak-semu". Sebuah Pseudo-Random Number Generator benar-benar menghasilkan pseudo-acak nomor urut .
Ketika para ahli berbicara tentang kualitas PRNG mereka benar-benar berbicara tentang sifat statistik dari urutan yang dihasilkan (dan sub-urutan yang penting). Misalnya jika Anda menggabungkan dua PRNG berkualitas tinggi dengan menggunakan keduanya secara bergantian - Anda dapat menghasilkan urutan hasil yang buruk - meskipun mereka menghasilkan urutan yang baik masing-masing secara terpisah (dua urutan yang baik mungkin hanya berkorelasi satu sama lain dan dengan demikian menggabungkan dengan buruk).
Urutan pseudo-acak sebenarnya selalu deterministik (ditentukan sebelumnya oleh algoritma dan parameter awal) yaitu sebenarnya tidak ada yang acak tentang hal itu.
Secara khusus rand()
/ srand(s)
sepasang fungsi menyediakan urutan nomor pseudo-acak pseudo-acak tunggal per-proses tunggal yang dihasilkan dengan algoritma yang ditentukan-implementasi. Fungsi rand()
menghasilkan nilai dalam rentang [0, RAND_MAX]
.
Kutipan dari standar C11:
The srand
fungsi menggunakan argumen sebagai benih untuk urutan baru angka pseudo-random akan dikembalikan oleh panggilan berikutnya ke rand
. Jika
srand
kemudian dipanggil dengan nilai seed yang sama, urutan bilangan pseudo-acak harus diulang. Jika rand
dipanggil sebelum panggilan apa pun srand
telah dibuat, urutan yang sama harus dihasilkan seperti ketika srand
pertama kali dipanggil dengan nilai seed 1.
Banyak orang cukup berharap bahwa rand()
akan menghasilkan urutan semi-independen nomor terdistribusi secara merata dalam kisaran 0
untuk RAND_MAX
. Yah itu pasti harus (kalau tidak itu tidak berguna) tapi sayangnya tidak hanya standar tidak mengharuskan - bahkan ada penyangkalan eksplisit yang menyatakan "tidak ada jaminan untuk kualitas urutan acak yang dihasilkan" . Dalam beberapa kasus sejarah rand
/srand
implementasi memang kualitasnya sangat buruk. Meskipun dalam implementasi modern kemungkinan besar cukup baik - tetapi kepercayaan itu rusak dan tidak mudah untuk pulih. Selain itu sifatnya yang non-thread-safe membuat penggunaannya yang aman dalam aplikasi multi-thread menjadi sulit dan terbatas (masih mungkin - Anda dapat menggunakannya dari satu utas khusus).
Template kelas baru std :: mersenne_twister_engine <> (dan kemudahan mengetiknya - std::mt19937
/ std::mt19937_64
dengan kombinasi parameter template yang baik) menyediakan per-objek generator nomor pseudo-acak didefinisikan dalam standar C ++ 11. Dengan parameter templat yang sama dan parameter inisialisasi yang sama, objek yang berbeda akan menghasilkan urutan output per objek yang persis sama pada komputer apa pun di aplikasi apa pun yang dibangun dengan pustaka standar yang memenuhi standar C ++ 11. Keuntungan dari kelas ini adalah urutan output yang diprediksi berkualitas tinggi dan konsistensi penuh di seluruh implementasi.
Juga ada lebih banyak mesin PRNG yang didefinisikan dalam standar C ++ 11 - std :: linear_congruential_engine <> (secara historis digunakan sebagai srand/rand
algoritma kualitas adil dalam beberapa implementasi perpustakaan standar C) dan std :: subtract_with_carry_engine <> . Mereka juga menghasilkan urutan output per objek yang ditentukan-parameter sepenuhnya ditentukan.
Modern C + + 11 contoh pengganti untuk kode C usang di atas:
#include <iostream>
#include <chrono>
#include <random>
int main()
{
std::random_device rd;
// seed value is designed specifically to make initialization
// parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
// different across executions of application
std::mt19937::result_type seed = rd() ^ (
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
).count() +
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count() );
std::mt19937 gen(seed);
for( unsigned long j = 0; j < 100500; ++j )
/* ^^^Yes. Generating single pseudo-random number makes no sense
even if you use std::mersenne_twister_engine instead of rand()
and even when your seed quality is much better than time(NULL) */
{
std::mt19937::result_type n;
// reject readings that would make n%6 non-uniformly distributed
while( ( n = gen() ) > std::mt19937::max() -
( std::mt19937::max() - 5 )%6 )
{ /* bad value retrieved so get next one */ }
std::cout << n << '\t' << n % 6 + 1 << '\n';
}
return 0;
}
Versi kode sebelumnya yang menggunakan std :: uniform_int_distribution <>
#include <iostream>
#include <chrono>
#include <random>
int main()
{
std::random_device rd;
std::mt19937::result_type seed = rd() ^ (
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
).count() +
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count() );
std::mt19937 gen(seed);
std::uniform_int_distribution<unsigned> distrib(1, 6);
for( unsigned long j = 0; j < 100500; ++j )
{
std::cout << distrib(gen) << ' ';
}
std::cout << '\n';
return 0;
}