Tüm tarafları görmek için yuvarlayın!


10

Diyelim ki 20 taraflı bir paranız var. O ölüyü yuvarlamaya başlıyorsunuz ve sonunda 20 değerin hepsini yuvarlamadan önce birkaç düzine kez yuvarlamanız gerekiyor. Acaba, 20 değerin hepsini% 50 görme şansım olmadan önce kaç ruloya ihtiyacım var? Ve ntüm ntarafları yuvarlamadan önce kaç rulo taraflı kalıp almam gerekiyor ?

Biraz araştırma yaptıktan sonra, rulolardan sonra tüm değerleri yuvarlama şansını hesaplamak için bir formül bulunduğunu öğrenirsiniz.nr

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

burada S(a, b)O anlamına gelir , ikinci türden numara Stirling yollarla k sayısı boş olmayan alt-(kenarda) içine n-nesneler (her bir rulo) bir dizi bölme.

Ayrıca , arayacağımız OEIS dizisini en az% 50 R(n)olan en küçük ryere karşılık geliyor P(r, n). Zor olan n, bu sekansın terimini olabildiğince hızlı hesaplamaktır .

Meydan okuma

  • A verildiğinde n, % 50'ye eşit veya daha büyük olan en küçük r yeri bulun .P(r, n)0.5
  • Kodunuz teorik nolarak negatif olmayan herhangi bir tamsayıyı girdi olarak ele almalıdır , ancak kodunuzu yalnızca aralığında test edeceğiz 1 <= n <= 1000000.
  • Puanlama için, R(n)girdilerin 1üzerinden geçmek için gereken toplam süreyi alacağız 10000.
  • Çözümlerinizi bizim sürümü çalıştırarak doğru olup olmadığını kontrol edecektir R(n)görmek için çıkışına eğer P(your_output, n) >= 0.5ve P(your_output - 1, n) < 0.5, yani çıktı aslında küçük olduğunu r, belirli bir için n.
  • S(a, b)Çözümünüz için herhangi bir tanım kullanabilirsiniz . Wikipedia'nın burada yardımcı olabilecek birkaç tanımı vardır.
  • Çözümlerinizi hesaplayanlar S(a, b)ve hatta P(r, n)doğrudan hesaplayanlar da dahil olmak üzere yerleşik olarak kullanabilirsiniz .
  • R(n)Bunların hiçbiri zor sınırlar olmasa da 1000 değerine ve bir milyon Stirling sayısına kadar kod yazabilirsiniz ve bunları yükseltmek veya indirmek için ikna edici bir argüman yapabiliyorsanız değiştirilebilir.
  • Olası her kontrol etmek gerekmez rarasında nve raradığımız ama en küçük bulmak için gereğini yapmak rve sadece herhangi bir ryerde P(r, n) >= 0.5.
  • Programınız Windows 10'da serbestçe çalıştırılabilen bir dil kullanmalıdır.

Çözümlerinizi test edecek bilgisayarın özellikleri i7 4790k, 8 GB RAM. @DJMcMayhem'e bilgisayarını test için sağladığı için teşekkürler . Referans için kendi resmi olmayan zamanlamanızı eklemekten çekinmeyin , ancak DJ test edebildikten sonra resmi zamanlama daha sonra sağlanacaktır.

Test senaryoları

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

Herhangi bir sorunuz veya öneriniz varsa bize bildirin. İyi şanslar ve iyi optimizasyon!


1
@JonathanAllan Farklı bir ifade seçmem gerektiğini biliyordum. Söylediğin için teşekkürler.
Sherlock9

Yanıtlar:


7

Python + NumPy, 3.95 Saniye

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)))

Çevrimiçi deneyin!

Nasıl çalışır

İçin kapalı form serisi Bu kullanım , P ( r , n ), ve benzerleri ile ilgili türev r , sayısal stabilite ve vektörleştirme için, yeniden düzenlenmiş bir Newton yöntemi arama yapmak için r , öyle ki p ( r , n ) = 0.5, yuvarlama r, her bir aşama önce bir tamsayıya, aşama hareket kadar r az 3/4. İyi bir ilk tahminle, bu genellikle sadece bir veya iki tekrar alır.

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 günlüğü (( n - i - 1) / n)
z ix i + 1 = d / d r P ( r , n )


1
Tüm cevap üzerinde mükemmel çalışma! Birincisi, fark olmalı 0.366512idi logşey yaştan önce. -log(log(2)Bir sonraki yinelemede kullanacağım . İkincisi, Newton'un yöntemini kullanma fikri de çok zekidir ve bunun çok iyi çalıştığını görmekten memnunum. Üçüncüsü, neredeyse kesinlikle çalarım exp(log(binom(n, i+1)) + r * log((n-i-1)/n)): P Kudos harika bir cevap! : D
Sherlock9

1
Resmi zamanlamayı ekledim! Güzel cevap BTW :)
James

2
Gerçekten kafam karıştı. Değiştim numpyiçin ithalat from numpy import *... zaman temelde 0'a düştü nedense ve için çevrimiçi Deneyin ?
notjagan

@notjagan önbellek belki vurmak?
NoOneIsHere

1
Birkaç şey için özür dilemek istiyorum: 1) iyileştirmeler bulmaya çalışırken cevabımdaki intihalim; 2) düzgün bir şekilde sahip olmama ve sadece cevabımı düzeltmeye çalışıyorum; 3) bu özür çok uzun sürdü. Öyle düştüm ki, ilk başta bu meydan okumayı terk ettim. Telafi etmeye yönelik küçük bir girişimde, bu cevabın ana gelişiminin Newton'un yönteminden artırmaya dönüştüğünü söylemek adil olur r, çünkü ilk yaklaşımınız zaten oldukça iyidir. Sizi bir kez daha PPCG'de görmeyi umuyoruz ve her şey için üzgünüm.
Sherlock9
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.