Algoritma yang paling efisien untuk mencetak 1-100 menggunakan generator angka acak yang diberikan

11

Kami diberi generator bilangan acak RandNum50yang menghasilkan bilangan bulat acak seragam di kisaran 1-50. Kami hanya dapat menggunakan generator angka acak ini untuk menghasilkan dan mencetak semua bilangan bulat dari 1 hingga 100 dalam urutan acak. Setiap angka harus tepat satu kali, dan probabilitas setiap angka yang terjadi di tempat mana pun harus sama.

Apa algoritma yang paling efisien untuk ini?

Raj Wadhwa
sumber
1
Gunakan array / atau bit vektor untuk merekam angka yang sudah terlihat dan penghitung untuk mencatat jumlah angka unik yang terlihat.
Dave Clarke
@DaveClarke Bagaimana saya bisa menghasilkan angka yang lebih besar dari 50 dengan itu? Jika saya menggunakannya lebih dari 1 kali, maka juga, bagaimana saya menghasilkan 1 menggunakannya?
Raj Wadhwa
1
Tantangannya tentu saja adalah memastikan bahwa semua tempat terjadi dengan kemungkinan yang sama. Anda bisa menggunakannya RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1).
Dave Clarke
2
@DaveClarke: Jadi Anda mengusulkan pengambilan sampel penolakan berulang? Itu akan berakhir hanya dengan harapan.
Raphael
Saya hanya memberi petunjuk.
Dave Clarke

Jawaban:

3

Saya pikir (jadi bisa salah :-) dari solusi yang menggunakan shuffle Fisher-Yates . Untuk menjaga distribusi yang seragam dengan pendekatan yang baik (lihat bagian EDIT di bawah) di setiap iterasi Anda dapat menggunakan trik ini untuk menghasilkan nilai antara 0 dan k - 1 :O(N2)krand0k1

 // return a random number in [0..k-1] with uniform distribution
 // using a uniform random generator in [1..50]
 funtion krand(k) {    
   sum = 0
   for i = 1 to k do sum = sum + RandNum50() - 1
   krand = sum mod k
 }

Algoritma Fisher-Yates menjadi:

arr : array[0..99]
for i = 0  to 99 do arr[i] = i+1; // store 1..100 in the array
for i = 99 downto 1 {
  r = krand(i+1)  // random value in [0..i]
  exchange the values of arr[i] and arr[r]
}
for i = 0 to 99 do print arr[i]

EDIT:

krandm=log2(k)rk

function trulyrand(k) {
    if (k <= 1) return 0
    while (true) { // ... if you're really unlucky ...
      m = ceil(log_2 (k) ) // calculate m such that k < 2^m
      r = 0  // will hold the random value
      while (m >= 0) {  // ... will add m bits        
        if ( rand50() > 25 ) then b = 1 else b = 0   // random bit
        r = r * 2 + b  // shift and add the random bit
        m = m - 1
      }      
      if (r < k) then return r  // we have 0<=r<2^m ; accept it, if r < k
    }
}
Vor
sumber
1
O(n)
1
Saya pikir "shuffle" adalah kata kunci utama di sini.
Raphael
4
Trik dalam krand (k) tidak menghasilkan distribusi yang benar-benar seragam, meskipun merupakan perkiraan yang baik: bahkan untuk k = 3 ini memberikan peluang 33,333328% untuk menghasilkan 0. Apakah ada justifikasi untuk menjumlahkan semua jalan hingga k di sini ? Saya akan berpikir batas yang lebih kecil sudah cukup jika kita hanya menginginkan perkiraan.
Erick Wong
1
@ ErickWong: Anda benar; Saya pikir distribusi seragam yang sebenarnya hanya dapat dicapai dengan menggunakan metode sampel penolakan yang tidak dijamin akan berakhir dalam waktu yang konstan. Ada skema perkiraan lain (yang memungkinkan untuk mencapai perkiraan yang diinginkan), yang saya usulkan adalah yang pertama kali muncul di pikiran saya.
Vor
2
r1..100rr
4

Karena orang lain telah memberikan solusi perkiraan dan solusi yang melibatkan pengambilan jumlah penyimpangan yang tidak pasti, bagaimana dengan bukti bahwa tidak ada algoritma yang dijamin hanya membutuhkan jumlah RandNum50()panggilan yang terbatas ?

1100!

kRandNum50kkRandNum50kkRandNum50(r1,r2,,rk)150kc50kc1100!100!50kk100!50k

Steven Stadnicki
sumber
2

nlogn+O(1)

if ( rand50() > 25 ) then b = 1 else b = 0   // random bit

1n!123n

Jika Anda tidak tahu cara membuat seragam, seperti yang disarankan dalam posting itu, dari bit acak, Anda juga bisa menghasilkan perkiraan seragam secara langsung, dengan cara ini (yang setara dengan "reallyrand" Vor, tetapi lebih cepat):

P = (RandNum50()-1) + (RandNum50()-1)*50^1 + (RandNum50()-1)*50^2 + ...

P50PQ=Pmodnn=100!P>n

Jérémie
sumber
1

Saya belum melakukan analisis untuk mengkonfirmasi seberapa seragam (atau tidak) ini akan, dan itu bisa disesuaikan menjadi shuffle yang benar, tetapi bisakah Anda memilih, dari susunan awal dari iindeks th = i + 1, (k + RandNum50() + RandNum50() - 1) mod (100 - k)indeks, dengan penghapusan, untuk k= 0,99?

Ini "mendorong" puncak RandNum50() + RandNum50()distribusi ke depan secara seragam.

Saya cukup yakin ini tidak tepat seperti yang saya nyatakan karena 0 index (1) tidak dapat diperoleh dari pilihan pertama dan saya tidak dapat dengan cepat melihat alternatif 1..50 + 1..50 penyesuaian yang menghasilkan 0 ..99.

Memperbarui

Untuk memperbaiki masalah yang saya catat, saya secara efektif digunakan RandNum100seperti yang disebutkan dalam komentar pertanyaan untuk menginisialisasi koffset pertama secara acak .

Ini menghasilkan distribusi dengan gelombang yang signifikan di bagian depan.

Alih-alih maju dengan 1 saya menggunakan yang lain RandNum50untuk meningkatkan itu terlebih dahulu k. Ini menghasilkan hasil yang cukup acak bagi saya, tetapi masih tidak "benar-benar" acak, seperti yang dapat dengan mudah dilihat jika Anda mengubah K menjadi 2.

Menguji kode VB.NET di mana saya melayani bahkan K. Catatan itu adalah O (K), 6K + 2 sebenarnya.

Mark Hurd
sumber