転がしてすべての面を見てください!


10

あなたが20面のサイコロを持っているとしましょう。あなたはそのサイコロを転がし始め、最終的に20個すべての値を転がす前に数十回転がさなければなりません。20個すべての値が50%の確率で表示されるまでに、いくつのロールが必要ですか。そして、n私がすべてのn面を転がす前に、何面のサイコロを振る必要がありますか?

いくつかの調査の結果、ロール後にすべての値をロールする可能性を計算するための式が存在することがわかりました。nr

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

ここで、は第2種のスターリング数をS(a, b)示し、n個のオブジェクト(各ロール)のセットをk個の空でないサブセット(各サイド)に分割する方法の数を示します。

また、が50%以上の最小のに対応するOEISシーケンス(これをと呼びます)も見つかります。課題は、このシーケンスのth項をできるだけ速く計算することです。R(n)rP(r, n)n

チャレンジ

  • 与えられn、検索最小 r場所P(r, n)以上で0.550%。
  • コードは理論的には負でない整数nを入力として処理する必要がありますが、テストするのはの範囲でのみ1 <= n <= 1000000です。
  • スコアリングのために、我々は実行するために必要な合計時間かかりなりますR(n)入力に1スルー10000
  • あなたのソリューションは、私たちのバージョンを実行して、正しいかどうかをチェックするR(n)かどうかを確認するために、あなたの出力にP(your_output, n) >= 0.5し、P(your_output - 1, n) < 0.5あなたの出力は、実際に最も小さいすなわちことを、r与えられたためn
  • S(a, b)ソリューションでは任意の定義を使用できます。ウィキペディアには、ここで役立ついくつかの定義があります。
  • S(a, b)計算P(r, n)するものや直接計算するものを含め、ソリューションに組み込みを使用できます。
  • 最大1000個の値R(n)と100万個のスターリング数をハードコードできますが、どちらもハードリミットではなく、値を上げたり下げたりする説得力のある引数を作成できる場合は変更できます。
  • と探しているのrnで可能な限りすべてをチェックする必要はありませんが、どこかだけでなくr、最小のものを見つける必要があります。rrP(r, n) >= 0.5
  • プログラムでは、Windows 10で自由に実行できる言語を使用する必要があります。

ソリューションをテストするコンピュータの仕様は次のとおりi7 4790k, 8 GB RAMです。テスト用のコンピューターを提供してくれた@DJMcMayhemに感謝します。参考のために独自の非公式のタイミングを自由に追加してください。ただし、公式のタイミングは、DJがテストできるようになり次第提供されます。

テストケース

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

質問や提案がありましたらお知らせください。幸運と最適化!


1
@JonathanAllan私は別の言い回しを選ぶべきだったことを知っていました。ヘッドアップをありがとう。
Sherlock9

回答:


7

Python + NumPy、3.95秒

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

オンラインでお試しください!

使い方

これは、Prn)の閉形式系列と、数値安定性とベクトル化のために再配置されたrに関するその導関数を使用して、Prn)= 0.5になるようなrのニュートン法検索を行います。ステップがrを3/4未満移動するまで、各ステップの前にrを整数に設定します。初期推測が適切であれば、これは通常1回または2回の反復で済みます。

X iが =ログ(1 - I / N)=ログ((N - I)/ N
CX I =ログ(N /((!N - I - !1)⋅ N I + 1
Y I = CX I + cx ni − 2cx n − 1 = log binom(ni + 1)
z i =(-1)i + 1⋅binom(ni + 1)⋅((ni − 1)/ nr
1 + ∑ z i = n!⋅ SRN)/ N R = PRN
Z IX I + 1 =(-1)I + 1 ⋅binom(NI + 1)⋅((N - I - 1) / nr log((ni − 1)/ n
Σ Z IX I + 1 = D / D Rの PRN


1
答え全体で素晴らしい仕事です!まず、私はそれ0.366512log何年も前のことだと気づくべきでした。-log(log(2)次の反復で使用します。第二に、ニュートンの方法を使用するという考えも非常に賢明であり、これが非常にうまく機能することを知ってうれしいです。第三に、私はほぼ間違いなくexp(log(binom(n, i+1)) + r * log((n-i-1)/n)):P Kudos を盗むつもりです。:D
Sherlock9

1
公式タイミング追加しました!正解ですBTW :)
James

2
私は本当に混乱しています。numpyインポートを変更し、from numpy import *何らかの理由で時間が基本的に0になりました... オンラインで試してみませんか?
notjagan 2017

@notjaganキャッシュヒットかな?
NoOneIsHere 2017

1
いくつかお詫び申し上げます。1)改善策を見つけようとしたときの私の答えの盗用。2)それを正しく所有しておらず、私の答えを修正しようとしているだけ。3)この謝罪には長い時間がかかりました。私はとても悔しかったので、最初はこの挑戦をやめました。わずかな賠償の試みでrは、最初の近似はすでにかなり良好であるため、この答えに対する私の主な改善点はニュートンの方法からインクリメントへの変更であったと言って差し支えないと思います。もう一度PPCGでお会いしましょう。
Sherlock9
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.