Diberikan fungsi yang menghasilkan bilangan bulat acak dalam kisaran 1 hingga 5, tulis fungsi yang menghasilkan bilangan bulat acak dalam kisaran 1 hingga 7.
- Apa itu solusi sederhana?
- Apa solusi yang efektif untuk mengurangi penggunaan memori atau berjalan pada CPU yang lebih lambat?
7 * rand5() / 5
?Jawaban:
Ini setara dengan solusi Adam Rosenfield, tetapi mungkin sedikit lebih jelas bagi sebagian pembaca. Ini mengasumsikan rand5 () adalah fungsi yang mengembalikan bilangan bulat acak secara statistik dalam kisaran 1 hingga 5 inklusif.
Bagaimana cara kerjanya? Pikirkan seperti ini: bayangkan mencetak array dua dimensi ini di atas kertas, menempelkannya ke papan panah dan melemparkan panah secara acak ke sana. Jika Anda menekan nilai bukan nol, itu adalah nilai acak statistik antara 1 dan 7, karena ada jumlah yang sama dari nilai bukan nol untuk dipilih. Jika Anda menekan nol, terus lempar panah sampai Anda memukul nol. Itulah yang dilakukan kode ini: indeks i dan j secara acak memilih lokasi pada papan panah, dan jika kami tidak mendapatkan hasil yang baik, kami terus melempar panah.
Seperti kata Adam, ini bisa berjalan selamanya dalam kasus terburuk, tetapi secara statistik, kasus terburuk tidak pernah terjadi. :)
sumber
rand5
seragam, setiap sel divals
grid memiliki probabilitas yang sama untuk dipilih. Kotak berisi persis tiga salinan dari masing-masing bilangan bulat dalam interval [1, 7], ditambah empat nol. Jadi aliran "mentah" hasil cenderung ke campuran nilai [1, 7] yang rata, ditambah beberapa angka nol yang terjadi sedikit lebih sering daripada nilai individual yang diizinkan. Tapi itu tidak masalah karena nol dilucuti, hanya menyisakan campuran nilai [1, 7] yang merata.Tidak ada solusi (tepat benar) yang akan berjalan dalam jumlah waktu yang konstan, karena 1/7 adalah desimal tak terbatas dalam basis 5. Salah satu solusi sederhana adalah dengan menggunakan sampel penolakan, misalnya:
Ini memiliki runtime yang diharapkan dari 25/21 = 1,19 iterasi loop, tetapi ada kemungkinan sangat kecil untuk mengulang selamanya.
sumber
N
panggilanrand5()
dalam kasus terburuk. Kemudian, ada 5 ^ N hasil yang mungkin dari urutan panggilanrand5
, yang masing-masing memiliki output 1-7. Jadi, jika Anda menjumlahkan semua kemungkinan urutan panggilan yang outputnyak
untuk setiap 1≤k≤7, maka probabilitas bahwa outputnyak
adalah m / 5 ^ N, di mana m adalah jumlah urutan tersebut. Jadi, m / 5 ^ N = 1/7, tetapi tidak ada solusi integer yang memungkinkan (N, m) untuk ini ==> kontradiksi.Saya ingin menambahkan jawaban lain, selain jawaban pertama saya . Jawaban ini mencoba untuk meminimalkan jumlah panggilan
rand5()
per panggilanrand7()
, untuk memaksimalkan penggunaan keacakan. Artinya, jika Anda menganggap keacakan sebagai sumber daya yang berharga, kami ingin menggunakan sebanyak mungkin itu, tanpa membuang bit acak. Jawaban ini juga memiliki beberapa kesamaan dengan logika yang disajikan dalam jawaban Ivan .The entropi dari suatu variabel acak adalah kuantitas didefinisikan dengan baik. Untuk variabel acak yang mengambil status N dengan probabilitas yang sama (distribusi seragam), entropinya adalah log 2 N. Dengan demikian,
rand5()
memiliki sekitar 2,32193 bit entropi, danrand7()
memiliki sekitar 2,80735 bit entropi. Jika kita berharap untuk memaksimalkan penggunaan keacakan kita, kita perlu menggunakan semua 2,32193 bit entropi dari setiap panggilanrand5()
, dan menerapkannya untuk menghasilkan 2,80735 bit entropi yang diperlukan untuk setiap panggilanrand7()
. Batas mendasar, maka, adalah bahwa kita tidak dapat melakukan lebih baik daripada log (7) / log (5) = 1,20906 panggilan kerand5()
per panggilan kerand7()
.Catatan samping: semua logaritma dalam jawaban ini akan menjadi basis 2 kecuali ditentukan sebaliknya.
rand5()
akan diasumsikan untuk mengembalikan angka dalam kisaran [0, 4], danrand7()
akan diasumsikan untuk mengembalikan angka dalam kisaran [0, 6]. Menyesuaikan rentang ke [1, 5] dan [1, 7] masing-masing adalah sepele.jadi bagaimana kita melakukannya? Kami menghasilkan bilangan real acak tak terhingga yang akurat antara 0 dan 1 (berpura-pura saat itu kami benar-benar dapat menghitung dan menyimpan bilangan yang tepat tak terhingga - kami akan memperbaikinya nanti). Kita dapat menghasilkan angka seperti itu dengan menghasilkan digitnya di basis 5: kita memilih angka acak 0.
a
1a
2a
3 ..., di mana setiap digit ai
dipilih oleh panggilan kerand5()
. Misalnya, jika RNG kami memilih ai
= 1 untuk semuai
, maka abaikan fakta bahwa itu tidak terlalu acak, itu akan sesuai dengan bilangan real 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (jumlah deret geometri).Ok, jadi kami telah memilih bilangan real acak antara 0 dan 1. Saya sekarang mengklaim bahwa bilangan acak tersebut didistribusikan secara seragam. Secara intuitif, ini mudah dimengerti, karena setiap digit dipilih secara seragam, dan jumlahnya sangat tepat. Namun, bukti formal ini agak lebih terlibat, karena sekarang kita berurusan dengan distribusi kontinu, bukan distribusi diskrit, jadi kita perlu membuktikan bahwa probabilitas bahwa nomor kita terletak pada interval [
a
,b
] sama dengan panjang interval itu,b - a
,. Buktinya dibiarkan sebagai latihan untuk pembaca =).Sekarang kita memiliki bilangan real acak yang dipilih secara seragam dari rentang [0, 1], kita perlu mengonversinya menjadi serangkaian angka acak seragam dalam rentang [0, 6] untuk menghasilkan output
rand7()
. Bagaimana kita melakukan ini? Kebalikan dari apa yang baru saja kita lakukan - kita mengonversinya menjadi desimal yang sangat tepat pada basis 7, dan kemudian setiap basis 7 digit akan sesuai dengan satu outputrand7()
.Mengambil contoh dari sebelumnya, jika kami
rand5()
menghasilkan aliran tak terbatas 1, maka bilangan real acak kami akan menjadi 1/4. Mengubah 1/4 ke basis 7, kita mendapatkan desimal tak terhingga 0.15151515 ..., jadi kita akan menghasilkan sebagai output 1, 5, 1, 5, 1, 5, dll.Oke, jadi kita punya ide utama, tetapi kita punya dua masalah tersisa: kita tidak bisa benar-benar menghitung atau menyimpan bilangan real yang sangat tepat, jadi bagaimana kita berurusan dengan hanya sebagian yang terbatas? Kedua, bagaimana kita benar-benar mengubahnya menjadi basis 7?
Salah satu cara kita dapat mengonversi angka antara 0 dan 1 ke basis 7 adalah sebagai berikut:
Untuk menangani masalah ketelitian tak terbatas, kami menghitung hasil parsial, dan kami juga menyimpan batas atas pada hasil apa yang mungkin terjadi. Artinya, misalkan kita sudah menelepon
rand5()
dua kali dan kembali 1 kali. Jumlah yang kami hasilkan sejauh ini adalah 0,11 (basis 5). Apa pun sisa dari serangkaian panggilan tak terbatas untukrand5()
diproduksi, bilangan real acak yang kami hasilkan tidak akan pernah lebih besar dari 0,12: selalu benar bahwa 0,11 ≤ 0,11xyz ... <0,12.Jadi, melacak nomor saat ini sejauh ini, dan nilai maksimum yang bisa diambil, kami mengonversi kedua angka menjadi basis 7. Jika mereka menyetujui
k
angka pertama , maka kami dapat dengan aman menampilkank
angka berikutnya - terlepas dari apa yang Aliran tak terbatas dari basis 5 digit adalah, mereka tidak akan pernah mempengaruhik
digit berikutnya dari representasi basis 7!Dan itulah algoritme - untuk menghasilkan output selanjutnya
rand7()
, kami hanya menghasilkan sebanyak digit yangrand5()
kami perlukan untuk memastikan bahwa kami tahu dengan pasti nilai digit berikutnya dalam konversi bilangan real acak ke basis 7. Berikut ini adalah implementasi Python, dengan test harness:Catatan yang
rand7_gen()
mengembalikan generator, karena memiliki keadaan internal yang melibatkan konversi angka ke basis 7. Uji harness memanggilnext(r7)
10.000 kali untuk menghasilkan 10.000 angka acak, dan kemudian mengukur distribusi mereka. Hanya matematika bilangan bulat yang digunakan, sehingga hasilnya persis benar.Perhatikan juga bahwa angka-angka di sini menjadi sangat besar, sangat cepat. Kekuatan 5 dan 7 tumbuh dengan cepat. Oleh karena itu, kinerja akan mulai menurun secara nyata setelah menghasilkan banyak angka acak, karena aritmatika bignum. Tapi ingat di sini, tujuan saya adalah untuk memaksimalkan penggunaan bit acak, bukan untuk memaksimalkan kinerja (meskipun itu adalah tujuan sekunder).
Dalam satu menjalankan ini, saya membuat 12091 panggilan ke
rand5()
untuk 10.000 panggilan kerand7()
, mencapai minimum panggilan log (7) / log (5) rata-rata untuk 4 angka signifikan, dan hasil yang dihasilkan seragam.Untuk mem-porting kode ini ke bahasa yang tidak memiliki bilangan bulat besar yang built-in, Anda harus membatasi nilai
pow5
danpow7
nilai maksimum tipe integral asli Anda - jika terlalu besar, maka setel ulang segalanya dan mulai lagi dari awal. Ini akan meningkatkan jumlah rata-rata panggilanrand5()
per panggilan menjadirand7()
sangat sedikit, tetapi mudah-mudahan itu tidak akan meningkat terlalu banyak bahkan untuk bilangan bulat 32 atau 64-bit.sumber
(Saya telah mencuri jawaban Adam Rosenfeld dan membuatnya berjalan sekitar 7% lebih cepat.)
Asumsikan bahwa rand5 () mengembalikan salah satu dari {0,1,2,3,4} dengan distribusi yang sama dan tujuannya adalah mengembalikan {0,1,2,3,4,5,6} dengan distribusi yang sama.
Kami melacak nilai terbesar yang dapat dihasilkan loop dalam variabel
max
. Jika reult sejauh ini adalah antara max% 7 dan max-1 maka hasilnya akan terdistribusi secara seragam dalam kisaran itu. Jika tidak, kami menggunakan sisanya, yang acak antara 0 dan maks% 7-1, dan panggilan lain ke rand () untuk membuat nomor baru dan maks baru. Lalu kita mulai lagi.Sunting: Mengharapkan beberapa kali untuk memanggil rand5 () adalah x dalam persamaan ini:
sumber
5 * rand5() + rand5()
.Algoritma:
7 dapat direpresentasikan dalam urutan 3 bit
Gunakan rand (5) untuk secara acak mengisi setiap bit dengan 0 atau 1.
Untuk mis: panggil rand (5) dan
jika hasilnya 1 atau 2, isi bit dengan 0
jika hasilnya 4 atau 5, isi bit dengan 1
jika hasilnya 3, abaikan dan lakukan lagi (penolakan)
Dengan cara ini kita dapat mengisi 3 bit secara acak dengan 0/1 dan karenanya mendapatkan angka dari 1-7.
EDIT: Ini sepertinya jawaban yang paling sederhana dan paling efisien, jadi inilah beberapa kode untuk itu:
sumber
sumber
Sunting: Itu tidak berhasil. Ini mati sekitar 2 bagian dalam 1000 (dengan asumsi rand5 sempurna). Ember mendapatkan:
Dengan beralih ke jumlah
tampaknya mendapatkan urutan besarnya untuk setiap 2 ditambahkan
BTW: tabel kesalahan di atas tidak dihasilkan melalui pengambilan sampel tetapi oleh relasi perulangan berikut:
sumber
sumber
ans += (r < 3) << i
Berikut ini menghasilkan distribusi seragam pada {1, 2, 3, 4, 5, 6, 7} menggunakan generator nomor acak yang menghasilkan distribusi seragam pada {1, 2, 3, 4, 5}. Kode ini berantakan, tetapi logikanya jelas.
sumber
Berbeda dengan solusi yang dipilih, algoritma akan berjalan dalam waktu yang konstan. Namun itu membuat 2 lebih banyak panggilan ke rand5 daripada rata-rata waktu berjalan dari solusi yang dipilih.
Perhatikan bahwa generator ini tidak sempurna (angka 0 memiliki peluang 0,0064% lebih banyak daripada angka lainnya), tetapi untuk sebagian besar tujuan praktis jaminan waktu konstan mungkin lebih besar daripada ketidakakuratan ini.
Penjelasan
Solusi ini berasal dari kenyataan bahwa angka 15.624 dapat dibagi oleh 7 dan dengan demikian jika kita dapat secara acak dan seragam menghasilkan angka dari 0 hingga 15.624 dan kemudian mengambil mod 7 kita bisa mendapatkan generator rand7 yang hampir seragam. Angka dari 0 hingga 15.624 dapat dihasilkan secara seragam dengan menggulirkan rand5 6 kali dan menggunakannya untuk membentuk digit nomor basis 5 sebagai berikut:
Namun, properti mod 7 memungkinkan kita untuk menyederhanakan persamaan:
Begitu
menjadi
Teori
Angka 15.624 tidak dipilih secara acak, tetapi dapat ditemukan menggunakan teorema kecil fermat, yang menyatakan bahwa jika p adalah bilangan prima maka
Jadi ini memberi kita,
(5 ^ 6) -1 sama dengan
Ini adalah angka dalam bentuk basis 5 dan dengan demikian kita dapat melihat bahwa metode ini dapat digunakan untuk beralih dari generator nomor acak apa pun ke generator nomor acak lainnya. Meskipun bias kecil terhadap 0 selalu diperkenalkan saat menggunakan eksponen p-1.
Untuk menggeneralisasikan pendekatan ini dan agar lebih akurat kita dapat memiliki fungsi seperti ini:
sumber
Apakah masalah pekerjaan rumah diperbolehkan di sini?
Fungsi ini tidak kasar "basis 5" matematika untuk menghasilkan angka antara 0 dan 6.
sumber
Jika kita mempertimbangkan kendala tambahan untuk mencoba memberikan jawaban yang paling efisien yaitu yang memberikan aliran input,,
I
bilangan bulat yang terdistribusi seragamm
dari 1-5 output aliranO
, bilangan bulat terdistribusi seragam dari 1-7 dari relatif panjang terpanjang untukm
, katakanL(m)
.Cara paling sederhana untuk menganalisis ini adalah dengan memperlakukan aliran I dan
O
sebagai bilangan 5-ary dan 7-ary masing-masing. Ini dicapai dengan ide jawaban utama untuk mengambil alirana1, a2, a3,... -> a1+5*a2+5^2*a3+..
dan juga untuk aliranO
.Kemudian jika kita mengambil bagian dari aliran input panjang di
m choose n s.t. 5^m-7^n=c
manac>0
dan sekecil mungkin. Lalu ada peta yang seragam dari aliran input panjang m ke bilangan bulat dari1
ke5^m
dan peta seragam lainnya dari bilangan bulat dari 1 ke7^n
aliran keluaran panjang n di mana kita mungkin harus kehilangan beberapa kasus dari aliran input ketika bilangan bulat dipetakan melebihi7^n
.Jadi ini memberikan nilai
L(m)
sekitarm (log5/log7)
yang kira-kira.82m
.Kesulitan dengan analisis di atas adalah persamaan
5^m-7^n=c
yang tidak mudah untuk memecahkan persis dan kasus di mana nilai seragam dari1
ke5^m
melebihi7^n
dan kami kehilangan efisiensi.Pertanyaannya adalah seberapa dekat dengan nilai terbaik m (log5 / log7) dapat dicapai. Sebagai contoh ketika angka ini mendekati mendekati integer, dapatkah kita menemukan cara untuk mencapai jumlah integral dari nilai output?
Jika
5^m-7^n=c
kemudian dari aliran input, kami secara efektif menghasilkan angka acak seragam dari0
ke(5^m)-1
dan tidak menggunakan nilai lebih tinggi dari7^n
. Namun nilai-nilai ini dapat diselamatkan dan digunakan lagi. Mereka secara efektif menghasilkan urutan angka yang seragam dari 1 hingga5^m-7^n
. Jadi kita dapat mencoba menggunakan ini dan mengubahnya menjadi angka 7-ary sehingga kita dapat membuat lebih banyak nilai output.Jika kita biarkan
T7(X)
menjadi panjang rata-rata dari urutan outputrandom(1-7)
bilangan bulat yang berasal dari ukuran input yang seragamX
, dan dengan asumsi itu5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
.Maka
T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
karena kita memiliki panjang tanpa urutan dengan probabilitas 7 ^ n0 / 5 ^ m dengan sisa panjang5^m-7^n0
dengan probabilitas(5^m-7^n0)/5^m)
.Jika kami terus mengganti, kami memperoleh:
Karenanya
Cara lain untuk mengatakan ini adalah:
Kasing terbaik adalah yang asli saya di atas di mana
5^m=7^n+s
, di manas<7
.Lalu
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
seperti sebelumnya.Kasus terburuk adalah ketika kita hanya dapat menemukan k dan st 5 ^ m = kx7 + s.
Kasus-kasus lain ada di suatu tempat di antara. Akan menarik untuk melihat seberapa baik kita dapat melakukan untuk m yang sangat besar, yaitu seberapa baik kita bisa mendapatkan istilah kesalahan:
Tampaknya mustahil untuk mencapainya
e(m) = o(1)
secara umum tetapi semoga kita bisa membuktikannyae(m)=o(m)
.Semuanya kemudian bertumpu pada distribusi digit 7-ary
5^m
untuk berbagai nilaim
.Saya yakin ada banyak teori di luar sana yang membahas hal ini. Saya mungkin akan melihat dan melaporkan kembali di beberapa titik.
sumber
Berikut ini adalah implementasi Python implementasi jawaban Adam .
Saya suka melempar algoritma yang saya lihat ke Python sehingga saya bisa bermain-main dengan mereka, saya pikir saya akan mempostingnya di sini dengan harapan itu berguna bagi seseorang di luar sana, bukan karena butuh waktu lama untuk dilempar bersama.
sumber
rand5()
PRNG yang layak, maka loop tidak akan terbatas karena pada akhirnya5*(rand5() - 1) + rand5()
pasti akan menjadi <= 21.)Mengapa tidak melakukannya secara sederhana?
Peluang mendapatkan 1 dan 7 dalam solusi ini lebih rendah karena modulo, namun, jika Anda hanya menginginkan solusi yang cepat dan mudah dibaca, inilah cara yang harus ditempuh.
sumber
Dengan asumsi bahwa rand (n) di sini berarti "bilangan bulat acak dalam distribusi seragam dari 0 hingga n-1 ", inilah contoh kode menggunakan randint Python, yang memiliki efek itu. Hanya menggunakan randint (5) , dan konstanta, untuk menghasilkan efek randint (7) . Sebenarnya agak konyol
sumber
do ... while
. Bisa jadi1337
, atau12345
, atau nomor apa pun> 1.Premis di balik jawaban yang benar Adam Rosenfield adalah:
Ketika n sama dengan 2, Anda memiliki 4 kemungkinan membuang: y = {22, 23, 24, 25}. Jika Anda menggunakan n sama dengan 6, Anda hanya memiliki 1 membuang: y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
Anda menelepon rand5 lebih banyak. Namun, Anda memiliki peluang jauh lebih rendah untuk mendapatkan nilai throw-away (atau infinite loop). Jika ada cara untuk tidak mendapatkan nilai membuang yang mungkin untuk y, saya belum menemukannya.
sumber
Inilah jawaban saya:
Ini sedikit lebih rumit daripada yang lain, tapi saya percaya ini meminimalkan panggilan ke rand5. Seperti dengan solusi lain, ada kemungkinan kecil bahwa itu bisa berulang untuk waktu yang lama.
sumber
Sederhana dan efisien:
(Terinspirasi oleh Apa kartun "programmer" favorit Anda? ).
sumber
Selama tidak ada tujuh kemungkinan yang tersisa untuk dipilih, gambarkan nomor acak lain, yang mengalikan jumlah kemungkinan dengan lima. Dalam Perl:
sumber
$possibilities
harus selalu bertambah menjadi 25 untuk keluar dari loop dan kembali. Jadi, hasil pertama Anda adalah[0-124] % 7
, yang tidak terdistribusi secara merata karena125 % 7 != 0
(ini sebenarnya 6).Saya tidak suka rentang mulai dari 1, jadi saya akan mulai dari 0 :-)
sumber
from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
Di sana Anda pergi, distribusi seragam dan nol panggilan Rand5.
Perlu mengatur benih sebelumnya.
sumber
Saya tahu itu telah dijawab, tetapi apakah ini tampaknya berhasil, tetapi saya tidak dapat memberi tahu Anda jika ada bias. 'Pengujian' saya menunjukkan itu, setidaknya, masuk akal.
Mungkin Adam Rosenfield akan berbaik hati berkomentar?
Ide saya (naif?) Adalah ini:
Akumulasi rand5 sampai ada bit acak yang cukup untuk membuat rand7. Ini memakan waktu paling banyak 2 rand5. Untuk mendapatkan nomor rand7 saya menggunakan nilai akumulasi mod 7.
Untuk menghindari overloading akumulator, dan karena akumulator adalah mod 7 maka saya mengambil mod 7 dari akumulator:
Fungsi rand7 () mengikuti:
(Saya membiarkan rentang rand5 menjadi 0-4 dan rand7 juga 0-6.)
Sunting: Hasil tambahan untuk 100 juta percobaan.
Fungsi 'Real' rand mod 5 atau 7
rand5: avg = 1.999802 0: 20003944 1: 19999889 2: 20003690 3: 19996938 4: 19995539 rand7: avg = 3.000111 0: 14282851 1: 14282851 1: 14282879 2: 14284554 3: 14288554 4: 14292388 5: 142292388 5: 14229853836 6: 14280046
Rand7 saya
Rata-rata terlihat ok dan distribusi angka juga terlihat ok.
Randand: rata-rata = 3,000080 0: 14288793 1: 14280135 2: 14287848 3: 14285277 4: 14286341 5: 14278663 6: 14292943
sumber
Ada beberapa algoritma elegan yang dikutip di atas, tetapi inilah salah satu cara untuk mendekatinya, meskipun mungkin bundaran. Saya mengasumsikan nilai yang dihasilkan dari 0.
R2 = generator angka acak yang memberikan nilai kurang dari 2 (ruang sampel = {0, 1})
R8 = generator angka acak yang memberikan nilai kurang dari 8 (ruang sampel = {0, 1, 2, 3, 4, 5, 5, 6, 7 })
Untuk menghasilkan R8 dari R2, Anda akan menjalankan R2 tiga kali, dan menggunakan hasil gabungan dari semua 3 berjalan sebagai angka biner dengan 3 digit. Berikut adalah rentang nilai ketika R2 dijalankan tiga kali:
0 0 0 -> 0
.
.
1 1 1 -> 7
Sekarang untuk menghasilkan R7 dari R8, kita cukup menjalankan R7 lagi jika mengembalikan 7:
Solusi bundaran adalah untuk menghasilkan R2 dari R5 (sama seperti kita menghasilkan R7 dari R8), kemudian R8 dari R2 dan kemudian R7 dari R8.
sumber
Berikut adalah solusi yang sepenuhnya sesuai dengan bilangan bulat dan berada dalam kisaran 4% dari yang optimal (yaitu menggunakan 1.26 angka acak dalam {0..4} untuk setiap orang di {0..6}). Kode dalam Scala, tetapi matematika harus cukup jelas dalam bahasa apa pun: Anda memanfaatkan fakta bahwa 7 ^ 9 + 7 ^ 8 sangat dekat dengan 5 ^ 11. Jadi Anda memilih angka 11 digit pada basis 5, dan kemudian menafsirkannya sebagai angka 9 digit pada basis 7 jika berada dalam kisaran (memberikan 9 basis 7 angka), atau sebagai angka 8 digit jika melebihi angka 9 digit, dll. .:
Jika Anda menempelkan tes ke interpreter (REPL sebenarnya), Anda mendapatkan:
Distribusinya bagus dan rata (dalam kisaran 10k 1/7 dari 10 ^ 8 di setiap nampan, seperti yang diharapkan dari distribusi kira-kira Gaussian).
sumber
Dengan menggunakan total bergulir , Anda dapat keduanya
Kedua masalah ini adalah masalah dengan
rand(5)+rand(5)...
solusi tipe sederhana . Kode Python berikut ini menunjukkan cara mengimplementasikannya (sebagian besar membuktikan distribusi).Dan output ini menunjukkan hasilnya:
Sederhana
rand(5)+rand(5)
, mengabaikan kasus-kasus di mana ini mengembalikan lebih dari 6 memiliki variasi khas 18%, 100 kali lipat dari metode yang ditunjukkan di atas:Dan, atas saran Nixuz, saya sudah membersihkan skrip sehingga Anda bisa mengekstrak dan menggunakan
rand7...
barang - barang:sumber
Jawaban ini lebih merupakan eksperimen dalam memperoleh entropi sebanyak mungkin dari fungsi Rand5. Oleh karena itu agak tidak jelas dan hampir pasti jauh lebih lambat daripada implementasi lainnya.
Dengan asumsi distribusi seragam dari 0-4 dan menghasilkan distribusi seragam dari 0-6:
Jumlah bit yang ditambahkan ke buffer per panggilan ke Rand5 saat ini 4/5 * 2 jadi 1,6. Jika nilai probabilitas 1/5 disertakan yang meningkat sebesar 0,05 maka 1,65 tetapi lihat komentar dalam kode di mana saya harus menonaktifkan ini.
Bit dikonsumsi melalui panggilan ke Rand7 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
Ini 3 + 3/8 + 3/64 + 3/512 ... jadi sekitar 3,42
Dengan mengekstraksi informasi dari tujuh, saya mendapatkan kembali 1/8 * 1/7 bit per panggilan jadi sekitar 0,018
Ini memberikan konsumsi bersih 3,4 bit per panggilan yang berarti rasionya adalah 2,125 panggilan ke Rand5 untuk setiap Rand7. Yang optimal harus 2.1.
Saya akan membayangkan pendekatan ini secara signifikan lebih lambat daripada banyak yang lain di sini kecuali biaya panggilan ke Rand5 sangat mahal (katakanlah memanggil beberapa sumber eksternal entropi).
sumber
dalam php
loop untuk menghasilkan angka acak antara 16 dan 127, membaginya dengan enam belas untuk membuat float antara 1 dan 7.9375, kemudian membulatkan ke bawah untuk mendapatkan int antara 1 dan 7. jika saya tidak salah, ada peluang 16/112 untuk mendapatkan salah satu dari 7 hasil.
sumber
sumber
7 = 111b
denganp(7) = 8 / 125
Saya pikir saya punya empat jawaban, dua memberikan solusi tepat seperti yang dari @Adam Rosenfield tetapi tanpa masalah loop tak terbatas, dan dua lainnya dengan solusi yang hampir sempurna tetapi implementasi lebih cepat daripada yang pertama.
Solusi tepat terbaik membutuhkan 7 panggilan
rand5
, tetapi mari kita lanjutkan untuk memahami.Metode 1 - Tepat
Kekuatan jawaban Adam adalah bahwa ia memberikan distribusi seragam yang sempurna, dan ada probabilitas yang sangat tinggi (21/25) bahwa hanya dua panggilan ke rand5 () yang akan dibutuhkan. Namun, kasus terburuk adalah infinite loop.
Solusi pertama di bawah ini juga memberikan distribusi seragam yang sempurna, tetapi membutuhkan total 42 panggilan ke
rand5
. Tidak ada loop tanpa batas.Berikut ini adalah implementasi R:
Bagi orang yang tidak terbiasa dengan R, ini adalah versi yang disederhanakan:
Distribusi
rand5
akan dipertahankan. Jika kita menghitungnya, masing-masing dari 7 iterasi loop memiliki 5 ^ 6 kombinasi yang memungkinkan, sehingga jumlah total kombinasi yang mungkin adalah(7 * 5^6) %% 7 = 0
. Dengan demikian kita dapat membagi angka acak yang dihasilkan dalam kelompok yang sama dari 7. Lihat metode dua untuk diskusi lebih lanjut tentang ini.Berikut ini semua kemungkinan kombinasi:
Saya pikir ini sangat jelas untuk menunjukkan bahwa metode Adam akan berjalan jauh lebih cepat. Probabilitas bahwa ada 42 atau lebih panggilan ke
rand5
dalam solusi Adam sangat kecil ((4/25)^21 ~ 10^(-17)
).Metode 2 - Tidak Tepat
Sekarang metode kedua, yang hampir seragam, tetapi membutuhkan 6 panggilan ke
rand5
:Ini adalah versi yang disederhanakan:
Ini pada dasarnya adalah satu iterasi dari metode 1. Jika kami menghasilkan semua kombinasi yang mungkin, berikut adalah jumlah yang dihasilkan:
Satu nomor akan muncul sekali lagi dalam
5^6 = 15625
uji coba.Sekarang, dalam Metode 1, dengan menambahkan 1 hingga 6, kami memindahkan angka 2233 ke setiap titik berurutan. Dengan demikian jumlah total kombinasi akan cocok. Ini berhasil karena 5 ^ 6 %% 7 = 1, dan kemudian kita melakukan 7 variasi yang sesuai, jadi (7 * 5 ^ 6 %% 7 = 0).
Metode 3 - Tepat
Jika argumen metode 1 dan 2 dipahami, metode 3 mengikuti, dan hanya membutuhkan 7 panggilan ke
rand5
. Pada titik ini, saya merasa ini adalah jumlah minimum panggilan yang diperlukan untuk solusi yang tepat.Berikut ini adalah implementasi R:
Bagi orang yang tidak terbiasa dengan R, ini adalah versi yang disederhanakan:
Distribusi
rand5
akan dipertahankan. Jika kita menghitungnya, masing-masing dari 7 iterasi loop memiliki 5 hasil yang mungkin, dengan demikian jumlah total kombinasi yang mungkin adalah(7 * 5) %% 7 = 0
. Dengan demikian kita dapat membagi angka acak yang dihasilkan dalam kelompok yang sama dari 7. Lihat metode satu dan dua untuk diskusi lebih lanjut tentang ini.Berikut ini semua kemungkinan kombinasi:
Saya pikir ini sangat jelas untuk menunjukkan bahwa metode Adam masih akan berjalan lebih cepat. Probabilitas bahwa ada 7 panggilan atau lebih
rand5
dalam solusi Adam masih kecil ((4/25)^3 ~ 0.004
).Metode 4 - Tidak Tepat
Ini adalah variasi kecil dari metode kedua. Hampir seragam, tetapi membutuhkan 7 panggilan ke
rand5
, itu adalah satu tambahan untuk metode 2:Ini adalah versi yang disederhanakan:
Jika kami menghasilkan semua kombinasi yang mungkin, berikut ini penghitungannya:
Dua angka akan muncul sekali kurang dalam
5^7 = 78125
uji coba. Untuk sebagian besar tujuan, saya bisa hidup dengan itu.sumber
i=7
juga tidak memiliki efek, karena menambahkan7*rand5()
ker
tidak mengubah nilair
mod 7.)Fungsi yang Anda butuhkan adalah rand1_7 () , saya menulis rand1_5 () sehingga Anda dapat mengujinya dan memplotnya.
sumber