Gulung untuk melihat semua sisi!

10

Katakanlah Anda memiliki dadu 20 sisi. Anda mulai menggulung dadu itu dan harus menggulungnya beberapa lusin kali sebelum akhirnya Anda menggulung semua 20 nilai. Anda bertanya-tanya, berapa banyak gulungan yang saya butuhkan sebelum mendapatkan peluang 50% untuk melihat semua 20 nilai? Dan berapa banyak gulungan ndadu sisi yang harus saya roll sebelum menggulung semua nsisi?

Setelah beberapa penelitian, Anda menemukan bahwa ada rumus untuk menghitung kemungkinan menggulirkan semua nnilai setelah rgulungan.

P(r, n) = n! * S(r, n) / n**r

di mana S(a, b)menunjukkan angka Stirling dari jenis kedua , jumlah cara untuk mempartisi sekumpulan objek n (masing-masing gulungan) ke dalam himpunan bagian yang tidak kosong (setiap sisi).

Anda juga menemukan urutan OEIS , yang akan kami panggil R(n), yang sesuai dengan yang terkecil di rmana P(r, n)setidaknya 50%. Tantangannya adalah menghitung njangka waktu urutan ini secepat mungkin.

Tantangan

  • Diberikan n, cari yang terkecil di r mana P(r, n)lebih besar atau sama dengan 0.5atau 50%.
  • Kode Anda secara teoritis harus menangani bilangan bulat non-negatif nsebagai input, tetapi kami hanya akan menguji kode Anda dalam kisaran 1 <= n <= 1000000.
  • Untuk mencetak gol, kami akan mengambil total waktu yang dibutuhkan untuk menjalankan R(n)pada input 1melalui 10000.
  • Kami akan memeriksa apakah solusi Anda sudah benar dengan menjalankan versi kami R(n)pada output Anda untuk melihat apakah P(your_output, n) >= 0.5dan P(your_output - 1, n) < 0.5, yaitu bahwa output Anda sebenarnya yang terkecil runtuk yang diberikan n.
  • Anda dapat menggunakan definisi apa pun untuk S(a, b)dalam solusi Anda. Wikipedia memiliki beberapa definisi yang mungkin bermanfaat di sini.
  • Anda dapat menggunakan built-in dalam solusi Anda, termasuk yang menghitung S(a, b), atau bahkan yang menghitung P(r, n)secara langsung.
  • Anda dapat melakukan hardcode hingga 1000 nilai R(n)dan sejuta angka Stirling, meskipun tidak satu pun dari ini adalah batas keras, dan dapat diubah jika Anda dapat membuat argumen yang meyakinkan untuk menaikkan atau menurunkannya.
  • Anda tidak perlu memeriksa setiap kemungkinan rantara ndan yang rkami cari, tetapi Anda perlu menemukan yang terkecil rdan tidak sembarang rtempat P(r, n) >= 0.5.
  • Program Anda harus menggunakan bahasa yang dapat dijalankan secara bebas di Windows 10.

Spesifikasi komputer yang akan menguji solusi Anda i7 4790k, 8 GB RAM. Terima kasih kepada @DJMcMayhem karena menyediakan komputernya untuk pengujian. Jangan ragu untuk menambahkan waktu tidak resmi Anda sendiri untuk referensi, tetapi waktu resmi akan diberikan nanti setelah DJ dapat mengujinya.

Uji kasus

n       R(n)
1       1
2       2
3       5
4       7
5       10
6       13
20      67       # our 20-sided die
52      225      # how many cards from a huge uniformly random pile until we get a full deck
100     497
366     2294     # number of people for to get 366 distinct birthdays
1000    7274
2000    15934
5000    44418
10000   95768
100000  1187943
1000000 14182022

Beri tahu saya jika Anda memiliki pertanyaan atau saran. Semoga berhasil dan optimisasi yang baik!

Sherlock9
sumber
1
@ JonathanAllan Tahu saya seharusnya memilih kata yang berbeda. Terimakasih atas peringatannya.
Sherlock9

Jawaban:

7

Python + NumPy, 3,95 Detik

from __future__ import division
import numpy as np

def rolls(n):
    if n == 1:
        return 1
    r = n * (np.log(n) - np.log(np.log(2)))
    x = np.log1p(np.arange(n) / -n)
    cx = x.cumsum()
    y = cx[:-1] + cx[-2::-1] - cx[-1]
    while True:
        r0 = np.round(r)
        z = np.exp(y + r0 * x[1:])
        z[::2] *= -1
        r = r0 - (z.sum() + 0.5) / z.dot(x[1:])
        if abs(r - r0) < 0.75:
            return np.ceil(r).astype(int)

for n in [1, 2, 3, 4, 5, 6, 20, 52, 100, 366, 1000, 2000, 5000, 10000, 100000, 1000000]:
    print('R({}) = {}'.format(n, rolls(n)))

import timeit
print('Benchmark: {:.2f}s'.format(timeit.timeit(lambda: sum(map(rolls, range(1, 10001))), number=1)))

Cobalah online!

Bagaimana itu bekerja

Ini menggunakan seri bentuk-tertutup untuk P ( r , n ), dan turunannya sehubungan dengan r , disusun ulang untuk stabilitas numerik dan vektorisasi, untuk melakukan pencarian metode Newton untuk r sehingga P ( r , n ) = 0,5, pembulatan r ke integer sebelum setiap langkah, sampai langkah bergerak r kurang dari 3/4. Dengan tebakan awal yang baik, ini biasanya hanya membutuhkan satu atau dua iterasi.

x i = log (1 - i / n ) = log (( n - i ) / n )
cx i = log ( n ! / (( n - i - 1)! ⋅ n i + 1 )
y i = cx i + cx n - i - 2 - cx n - 1 = log binom ( n , i + 1)
z i = (-1) i + 1 ⋅ binom ( n ,i + 1) ⋅ (( n - i - 1) / n ) r
1 + ∑ z i = n! ⋅ S ( r , n ) / n r = P ( r , n )
z ix i + 1 = (-1) i + 1 ⋅ binom ( n , i + 1) ⋅ (( n - i - 1) / n ) r log (( n - i - 1) / n)
z ix i + 1 = d / d r P ( r , n )

Anders Kaseorg
sumber
1
Kerja bagus untuk seluruh jawaban! Pertama, saya seharusnya menyadari bahwa 0.366512itu adalah logsesuatu yang sudah lama ada. Akan digunakan -log(log(2)dalam iterasi berikutnya. Kedua, gagasan untuk menggunakan metode Newton juga sangat pintar dan saya senang melihat bahwa ini bekerja dengan sangat baik. Ketiga, saya hampir pasti akan mencuri exp(log(binom(n, i+1)) + r * log((n-i-1)/n)): P Kudos pada jawaban yang bagus! : D
Sherlock9
1
Saya telah menambahkan waktu resmi! Jawaban bagus BTW :)
James
2
Saya sangat bingung. Saya mengubah numpyimpor ke from numpy import *dan untuk beberapa alasan waktu pada dasarnya turun ke 0 ... Coba online ?
notjagan
@notjagan cache hit mungkin?
NoOneIsHere
1
Saya ingin meminta maaf untuk beberapa hal: 1) plagiarisme jawaban Anda ketika saya mencoba menemukan perbaikan; 2) tidak memiliki hak untuk itu dan hanya mencoba untuk memperbaiki jawaban saya; 3) bahwa permintaan maaf ini sudah sangat lama. Saya sangat malu bahwa pada awalnya saya baru saja meninggalkan tantangan ini. Dalam upaya reparasi kecil, saya kira cukup adil untuk memberi tahu Anda bahwa perbaikan utama saya pada jawaban ini berubah dari metode Newton menjadi bertambah r, karena perkiraan awal Anda sudah cukup baik. Berharap dapat melihat Anda di PPCG sekali lagi, dan maaf untuk semuanya.
Sherlock9