Saya membutuhkan fungsi yang akan menghasilkan bilangan bulat acak dalam rentang yang diberikan (termasuk nilai batas). Saya tidak persyaratan kualitas / keacakan yang tidak masuk akal, saya memiliki empat persyaratan:
- Saya perlu cepat. Proyek saya perlu menghasilkan jutaan (atau kadang-kadang bahkan puluhan juta) angka acak dan fungsi generator saya saat ini telah terbukti menjadi hambatan.
- Saya membutuhkannya agar seragam (penggunaan rand () baik-baik saja).
- rentang min-max bisa apa saja dari <0, 1> hingga <-32727, 32727>.
- itu harus ditaburkan.
Saat ini saya memiliki kode C ++ berikut:
output = min + (rand() * (int)(max - min) / RAND_MAX)
Masalahnya adalah, itu tidak benar-benar seragam - maks dikembalikan hanya ketika rand () = RAND_MAX (untuk Visual C ++ itu 1/32727). Ini adalah masalah utama untuk rentang kecil seperti <-1, 1>, di mana nilai terakhir hampir tidak pernah dikembalikan.
Jadi saya mengambil pena dan kertas dan menghasilkan formula berikut (yang dibangun di atas (int) (n + 0,5) trik pembulatan bilangan bulat):
Tapi itu masih tidak memberi saya distribusi seragam. Berjalan berulang dengan 10.000 sampel memberi saya rasio 37:50:13 untuk nilai nilai -1, 0. 1.
Bisakah Anda menyarankan formula yang lebih baik? (atau bahkan seluruh fungsi generator nomor pseudo-acak)
Jawaban:
Solusi cepat, agak lebih baik dari milik Anda, tetapi masih belum terdistribusi dengan benar
Kecuali ketika ukuran kisaran adalah kekuatan 2, metode ini menghasilkan angka terdistribusi tidak bias yang bias terlepas dari kualitas
rand()
. Untuk uji komprehensif kualitas metode ini, baca ini .sumber
rand()
harus dianggap berbahaya di C ++ ada banyak cara yang lebih baik untuk mendapatkan sesuatu yang terdistribusi secara seragam dan sebenarnya acak.Jawaban C ++ yang paling sederhana (dan karenanya terbaik) (menggunakan standar 2011) adalah
Tidak perlu menemukan kembali roda. Tidak perlu khawatir tentang bias. Tidak perlu khawatir menggunakan waktu sebagai benih acak.
sumber
random_device
, yang mungkin benar-benar rusak dalam beberapa kasus . Selain itu,mt19937
walaupun merupakan pilihan tujuan umum yang sangat baik, bukan yang tercepat dari generator berkualitas baik (lihat perbandingan ini ) dan karenanya mungkin bukan kandidat yang ideal untuk OP.minstd
akan menjadi metode semacam itu), tapi itu kemajuan. Adapun implementasi yang buruk darirandom_device
- itu mengerikan dan harus dianggap bug (mungkin juga dari standar C ++, jika memungkinkan).rand()
bukanlah suatu pilihan, dan apakah itu penting untuk penggunaan yang tidak kritis, seperti menghasilkan indeks pivot acak? Juga, apakah saya harus khawatir tentang membangunrandom_device
/mt19937
/uniform_int_distribution
dalam fungsi loop / inline yang ketat? Haruskah saya lebih suka membagikannya?Jika kompiler Anda mendukung C ++ 0x dan menggunakannya adalah opsi untuk Anda, maka
<random>
tajuk standar baru cenderung memenuhi kebutuhan Anda. Ini memiliki kualitas tinggiuniform_int_distribution
yang akan menerima batas minimum dan maksimum (termasuk yang Anda butuhkan), dan Anda dapat memilih di antara berbagai generator angka acak untuk dihubungkan ke distribusi itu.Berikut adalah kode yang menghasilkan sejuta acak yang
int
didistribusikan secara seragam di [-57, 365]. Saya telah menggunakan<chrono>
fasilitas std baru untuk mengukur waktu seperti yang Anda sebutkan kinerja adalah perhatian utama bagi Anda.Bagi saya (2,8 GHz Intel Core i5) ini mencetak:
2.10268e + 07 angka acak per detik.
Anda dapat menyemai generator dengan memasukkan int ke konstruktornya:
Jika nanti Anda menemukan bahwa
int
itu tidak mencakup rentang yang Anda butuhkan untuk distribusi Anda, ini dapat diperbaiki dengan mengubahuniform_int_distribution
seperti itu (misalnya kelong long
):Jika nanti Anda menemukan bahwa
minstd_rand
generator itu tidak berkualitas cukup tinggi, itu juga dapat dengan mudah diganti. Misalnya:Memiliki kontrol terpisah atas generator angka acak, dan distribusi acak bisa sangat membebaskan.
Saya juga telah menghitung (tidak ditampilkan) 4 "momen" pertama dari distribusi ini (menggunakan
minstd_rand
) dan membandingkannya dengan nilai-nilai teoritis dalam upaya untuk mengukur kualitas distribusi:(
x_
Awalan mengacu pada "yang diharapkan")sumber
d
di setiap iterasi dengan batasan yang berbeda? Berapa banyak memperlambat loop?Mari kita bagi masalah menjadi dua bagian:
n
dalam rentang 0 hingga (maks-mnt).Bagian pertama jelas yang paling sulit. Mari kita asumsikan bahwa nilai pengembalian rand () sangat seragam. Menggunakan modulo akan menambah bias ke
(RAND_MAX + 1) % (max-min+1)
angka pertama . Jadi jika kita secara ajaib bisa berubahRAND_MAX
menjadiRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, tidak ada lagi akan bias.Ternyata kita dapat menggunakan intuisi ini jika kita bersedia mengizinkan pseudo-nondeterminisme ke dalam waktu berjalan dari algoritma kita. Setiap kali rand () mengembalikan nomor yang terlalu besar, kami cukup meminta nomor acak lain sampai kami mendapatkan nomor yang cukup kecil.
Waktu berjalan sekarang didistribusikan secara geometris , dengan nilai yang diharapkan di
1/p
manap
probabilitas mendapatkan angka yang cukup kecil pada percobaan pertama. KarenaRAND_MAX - (RAND_MAX + 1) % (max-min+1)
selalu kurang dari(RAND_MAX + 1) / 2
, kita tahu itup > 1/2
, sehingga jumlah iterasi yang diharapkan akan selalu kurang dari dua untuk rentang apa pun. Seharusnya dimungkinkan untuk menghasilkan puluhan juta angka acak dalam waktu kurang dari satu detik pada CPU standar dengan teknik ini.EDIT:
Meskipun hal di atas secara teknis benar, jawaban DSimon mungkin lebih berguna dalam praktiknya. Anda seharusnya tidak menerapkan hal ini sendiri. Saya telah melihat banyak implementasi dari sampel penolakan dan seringkali sangat sulit untuk melihat apakah itu benar atau tidak.
sumber
Bagaimana dengan Twister Mersenne ? Implementasi boost agak mudah digunakan dan diuji dengan baik di banyak aplikasi dunia nyata. Saya telah menggunakannya sendiri di beberapa proyek akademik seperti kecerdasan buatan dan algoritma evolusi.
Inilah contoh mereka di mana mereka membuat fungsi sederhana untuk menggulung dadu enam sisi:
Oh, dan inilah beberapa mucikari dari generator ini kalau-kalau Anda tidak yakin Anda harus menggunakannya pada yang jauh lebih rendah
rand()
:sumber
boost::uniform_int
distribusi), Anda dapat mengubah rentang min max menjadi apa pun yang Anda suka, dan itu dapat diunggulkan.Ini adalah pemetaan 32768 integer ke (nMax-nMin + 1) integer. Pemetaan akan cukup baik jika (nMax-nMin + 1) kecil (seperti dalam kebutuhan Anda). Perhatikan bahwa jika (nMax-nMin + 1) besar, pemetaan tidak akan berfungsi (Misalnya - Anda tidak dapat memetakan 32768 nilai ke nilai 30000 dengan probabilitas sama). Jika rentang tersebut diperlukan - Anda harus menggunakan sumber acak 32-bit atau 64-bit, alih-alih hasil rand 15-bit (), atau abaikan rand () yang berada di luar jangkauan.
sumber
RAND_MAX
ke(double) RAND_MAX
untuk menghindari peringatan integer overflow.Ini adalah versi yang tidak bias yang menghasilkan angka di
[low, high]
:Jika rentang Anda cukup kecil, tidak ada alasan untuk menembolok sisi kanan perbandingan di
do
loop.sumber
[0, h)
untuk kesederhanaan. Panggilanrand()
memilikiRAND_MAX + 1
nilai pengembalian yang memungkinkan; mengambilrand() % h
runtuh(RAND_MAX + 1) / h
dari mereka ke masing-masing nilaih
output, kecuali bahwa(RAND_MAX + 1) / h + 1
mereka dipetakan ke nilai yang kurang dari(RAND_MAX + 1) % h
(karena siklus parsial terakhir melaluih
output). Karena itu kami menghapus(RAND_MAX + 1) % h
kemungkinan output untuk mendapatkan distribusi yang tidak bias.Saya merekomendasikan perpustakaan Boost.Random , sangat rinci dan terdokumentasi dengan baik, memungkinkan Anda menentukan secara spesifik distribusi apa yang Anda inginkan, dan dalam skenario non-kriptografi sebenarnya dapat mengungguli implementasi rand C library yang khas.
sumber
anggap min dan maks adalah nilai int, [dan] berarti sertakan nilai ini, (dan) berarti tidak termasuk nilai ini, gunakan di atas untuk mendapatkan nilai yang tepat menggunakan c ++ rand ()
referensi: untuk () mendefinisikan], kunjungi:
https://en.wikipedia.org/wiki/Interval_(mathematics)
untuk fungsi rand dan srand atau yang ditentukan RAND_MAX, kunjungi:
http://en.cppreference.com/w/cpp/numeric/random/rand
[min, maks]
(minimum, maks)
[min, maks)
(minimum, maks)
sumber
Dalam thread penolakan sampel ini sudah dibahas, tetapi saya ingin menyarankan satu optimasi berdasarkan fakta yang
rand() % 2^something
tidak menimbulkan bias seperti yang telah disebutkan di atas.Algoritma ini sangat sederhana:
Ini kode contoh saya:
Ini bekerja dengan baik terutama untuk interval kecil, karena kekuatan 2 akan "lebih dekat" dengan panjang interval nyata, sehingga jumlah kesalahan akan lebih kecil.
PS
Jelas menghindari rekursi akan lebih efisien (tidak perlu menghitung berulang-ulang log plafon ..) tapi saya pikir itu lebih mudah dibaca untuk contoh ini.
sumber
Perhatikan bahwa dalam sebagian besar saran, nilai acak awal yang Anda dapatkan dari fungsi rand (), yang biasanya dari 0 hingga RAND_MAX, terbuang sia-sia. Anda hanya membuat satu angka acak, sementara ada prosedur suara yang bisa memberi Anda lebih banyak.
Asumsikan bahwa Anda menginginkan wilayah [min, maks] angka bilangan bulat acak. Kita mulai dari [0, maks-mnt]
Ambil basis b = maks-min + 1
Mulai dari mewakili angka yang Anda dapatkan dari rand () di pangkalan b.
Dengan cara itu Anda mendapatkan lantai (log (b, RAND_MAX)) karena setiap digit dalam basis b, kecuali mungkin yang terakhir, mewakili angka acak dalam kisaran [0, maks-mnt].
Tentu saja perubahan terakhir ke [min, maks] sederhana untuk setiap angka acak r + min.
Jika NUM_DIGIT adalah jumlah digit dalam basis b yang dapat Anda ekstrak dan itu
maka yang di atas adalah sebagai implementasi sederhana mengekstraksi NUM_DIGIT angka acak dari 0 ke b-1 dari satu RAND_MAX nomor acak yang menyediakan b <RAND_MAX.
sumber
Rumus untuk ini sangat sederhana, jadi coba ungkapan ini,
sumber
int num = (int) rand() % (max - min) + min;
Ungkapan berikut harus tidak bias jika saya tidak salah:
Saya berasumsi di sini bahwa rand () memberi Anda nilai acak dalam kisaran antara 0,0 dan 1,0 TIDAK termasuk 1,0 dan bahwa max dan min adalah bilangan bulat dengan kondisi bahwa min <max.
sumber
std::floor
kembalidouble
, dan kami membutuhkan nilai integer di sini. Saya hanya akan menggunakan untukint
bukannya menggunakanstd::floor
.