Pada dasarnya, yang Anda minta adalah generator acara "semi-acak" yang menghasilkan acara dengan properti berikut:
Tingkat rata-rata di mana setiap peristiwa terjadi ditentukan terlebih dahulu.
Peristiwa yang sama lebih kecil kemungkinannya terjadi dua kali berturut-turut daripada secara acak.
Peristiwa tidak sepenuhnya dapat diprediksi.
Salah satu cara untuk melakukannya adalah dengan mengimplementasikan generator peristiwa non-acak yang memenuhi tujuan 1 dan 2, dan kemudian menambahkan beberapa keacakan untuk memenuhi tujuan 3.
Untuk generator acara non-acak, kita dapat menggunakan algoritma dithering sederhana . Secara khusus, misalkan p 1 , p 2 , ..., p n menjadi kemungkinan relatif dari kejadian 1 ke n , dan mari s = p 1 + p 2 + ... + p n menjadi jumlah dari bobot. Kami kemudian dapat menghasilkan urutan kejadian yang secara acak disetarakan secara maksimal secara non-acak menggunakan algoritma berikut:
Awalnya, biarkan e 1 = e 2 = ... = e n = 0.
Untuk menghasilkan suatu peristiwa, tambah setiap e i dengan p i , dan output k acara yang e k terbesar (memutuskan ikatan apa pun yang Anda inginkan).
Pengurangan e k oleh s , dan ulangi dari langkah 2.
Sebagai contoh, mengingat tiga kejadian A, B dan C, dengan p A = 5, p B = 4 dan p C = 1, algoritma ini menghasilkan sesuatu seperti urutan output berikut:
A B A B C A B A B A A B A B C A B A B A A B A B C A B A B A
Perhatikan bagaimana urutan 30 peristiwa ini mengandung tepat 15 As, 12 Bs dan 3 Cs. Ini tidak cukup optimal mendistribusikan - ada beberapa kejadian dua As berturut-turut, yang bisa dihindari - tetapi semakin dekat.
Sekarang, untuk menambahkan keacakan ke urutan ini, Anda memiliki beberapa opsi (tidak harus saling eksklusif):
Anda dapat mengikuti saran Philipp , dan mempertahankan "setumpuk" acara N mendatang, untuk sejumlah N yang berukuran tepat . Setiap kali Anda perlu membuat acara, Anda memilih acara acak dari dek, dan kemudian menggantinya dengan output acara berikutnya dengan algoritma dithering di atas.
Menerapkan ini pada contoh di atas, dengan N = 3, menghasilkan misalnya:
A B A B C A B B A B A B C A A A A B B A B A C A B A B A B A
sedangkan N = 10 menghasilkan yang lebih acak:
A A B A C A A B B B A A A A A A C B A B A A B A C A C B B B
Perhatikan bagaimana peristiwa umum A dan B berakhir dengan lebih banyak berjalan karena pengocokan, sedangkan peristiwa C langka masih cukup baik spasi.
Anda dapat menyuntikkan beberapa keacakan langsung ke dalam algoritma dithering. Misalnya, alih-alih menambah e i dengan p i pada langkah 2, Anda dapat menambahkannya dengan p i × acak (0, 2), di mana acak ( a , b ) adalah nomor acak yang terdistribusi secara seragam antara a dan b ; ini akan menghasilkan output seperti berikut:
A B B C A B A A B A A B A B A A B A A A B C A B A B A C A B
atau Anda dapat menambah e i dengan p i + acak (- c , c ), yang akan menghasilkan (untuk c = 0,1 x dtk ):
B A A B C A B A B A B A B A C A B A B A B A A B C A B A B A
atau, untuk c = 0,5 × s :
B A B A B A C A B A B A A C B C A A B C B A B B A B A B C A
Perhatikan bagaimana skema aditif memiliki efek pengacakan yang jauh lebih kuat untuk peristiwa langka C daripada untuk peristiwa umum A dan B, dibandingkan dengan yang multiplikatif; ini mungkin atau mungkin tidak diinginkan. Tentu saja, Anda juga bisa menggunakan beberapa kombinasi dari skema ini, atau penyesuaian lainnya terhadap kenaikan, asalkan itu mempertahankan properti yang kenaikan rata - rata e i sama dengan p i .
Atau, Anda bisa mengganggu output dari algoritma dithering dengan kadang-kadang mengganti event yang dipilih k dengan yang acak (dipilih sesuai dengan bobot mentah p i ). Selama Anda juga menggunakan k yang sama pada langkah 3 seperti yang Anda hasilkan pada langkah 2, proses dithering masih cenderung meratakan fluktuasi acak.
Misalnya, inilah beberapa contoh hasil, dengan peluang 10% dari setiap acara dipilih secara acak:
B A C A B A B A C B A A B B A B A B A B C B A B A B C A B A
dan inilah contoh dengan peluang 50% dari setiap output secara acak:
C B A B A C A B B B A A B A A A A A B B A C C A B B A B B C
Anda juga dapat mempertimbangkan makan campuran kejadian murni acak dan ragu-ragu menjadi pencampuran kolam renang / deck, seperti dijelaskan di atas, atau mungkin mengacak algoritma dithering dengan memilih k secara acak, seperti ditimbang oleh e i s (memperlakukan bobot negatif nol).
Ps. Berikut adalah beberapa urutan peristiwa yang benar-benar acak, dengan tingkat rata-rata yang sama, untuk perbandingan:
A C A A C A B B A A A A B B C B A B B A B A B A A A A A A A
B C B A B C B A A B C A B A B C B A B A A A A B B B B B B B
C A A B A A B B C B B B A B A B A A B A A B A B A C A A B A
Tangent: Karena telah ada beberapa perdebatan dalam komentar tentang apakah perlu, untuk solusi berbasis dek, untuk memungkinkan geladak kosong sebelum diisi ulang, saya memutuskan untuk membuat perbandingan grafis dari beberapa strategi pengisian geladak:
Plot beberapa strategi untuk menghasilkan membalik koin semi-acak (dengan rata-rata rasio head to tail 50:50). Sumbu horizontal adalah jumlah flips, sumbu vertikal adalah jarak kumulatif dari rasio yang diharapkan, diukur sebagai (ekor - kepala) / 2 = kepala - membalik / 2.
Garis merah dan hijau pada plot menunjukkan dua algoritma berbasis non-dek untuk perbandingan:
- Garis merah, dithering deterministik : hasil yang bernomor genap selalu kepala, hasil angka ganjil selalu berekor.
- Garis hijau, flip acak independen : setiap hasil dipilih secara independen secara acak, dengan kemungkinan kepala 50% dan peluang ekor 50%.
Tiga baris lainnya (biru, ungu dan cyan) menunjukkan hasil dari tiga strategi berbasis dek, masing-masing diimplementasikan menggunakan setumpuk 40 kartu, yang awalnya diisi dengan 20 kartu "kepala" dan 20 kartu "ekor":
- Garis biru, isi ketika kosong : Kartu diambil secara acak hingga geladak kosong, lalu geladak diisi ulang dengan 20 kartu "kepala" dan 20 kartu "ekor".
- Garis ungu, isi ketika setengah kosong : Kartu digambar secara acak hingga dek memiliki 20 kartu tersisa; kemudian dek diisi dengan 10 kartu "kepala" dan 10 kartu "ekor".
- Baris cyan, isi terus menerus : Kartu diambil secara acak; undian bernomor genap segera diganti dengan kartu "kepala", dan undian bernomor ganjil dengan kartu "ekor".
Tentu saja, plot di atas hanyalah satu realisasi dari proses acak, tetapi cukup representatif. Secara khusus, Anda dapat melihat bahwa semua proses berbasis dek memiliki bias terbatas, dan tetap cukup dekat dengan garis merah (deterministik), sedangkan garis hijau murni acak akhirnya berkeliaran.
(Faktanya, penyimpangan garis biru, ungu dan cyan dari nol sangat dibatasi oleh ukuran dek: garis biru tidak pernah bisa melayang lebih dari 10 langkah dari nol, garis ungu hanya bisa mendapatkan 15 langkah dari nol , dan garis cyan dapat melayang paling jauh 20 langkah dari nol. Tentu saja, dalam praktiknya, salah satu garis yang benar-benar mencapai batasnya sangat tidak mungkin, karena ada kecenderungan kuat bagi mereka untuk kembali mendekati nol jika mereka berjalan terlalu jauh mati.)
Sekilas, tidak ada perbedaan yang jelas antara strategi berbasis dek yang berbeda (meskipun, rata-rata, garis biru tetap agak lebih dekat dengan garis merah, dan garis cyan berada agak jauh lebih jauh), tetapi pemeriksaan lebih dekat dari garis biru tidak mengungkapkan pola deterministik yang berbeda: setiap 40 undian (ditandai oleh garis vertikal abu-abu putus-putus), garis biru persis memenuhi garis merah di nol. Garis ungu dan cyan tidak dibatasi dengan ketat, dan dapat menjauhi nol pada titik mana pun.
Untuk semua strategi berbasis dek, fitur penting yang membuat variasi mereka tetap terikat adalah kenyataan bahwa, sementara kartu diambil dari dek secara acak, dek diisi ulang secara deterministik. Jika kartu yang digunakan untuk mengisi ulang dek itu sendiri dipilih secara acak, semua strategi berbasis dek akan menjadi tidak dapat dibedakan dari pilihan acak murni (garis hijau).