これは、まったく使用せずに解決するのが難しい問題ではないことに注意してくださいscipy
。10 個のアイテムのランダム順列があるとします。
4, 7, 2, 3, 0, 9, 1, 5, 6, 8
そして「勝者」のセット2, 4, 6
。気にするのは勝者と敗者だけなので、表現を少し単純化できます。
1, 0, 1, 0, 0, 0, 0, 0, 1, 0
10 個の可能なアイテムと 3 個の勝者の任意のセットで同じことができます。10 個のアイテムの可能な順列が与えられれば、同じ単純化を実行できます。つまり、実際に起こっていることは、各順列が 3 つの勝利インデックスを「選択」し、デッキ内の勝者の可能な配置の数が10で10! / (3! * 7!)
あるということです。
ここで知る必要があるのは、勝者の可能な配置のうち、最初のQ
カードで少なくとも 1 人の勝者が得られる数です。最初のカードで正確に0の勝者が得られる配置の数を計算する方が簡単なのでQ
、代わりにそれを計算します。したがって、最も具体的には、次のようなシーケンスの数が必要です (Q = 4 の場合)。
0, 0, 0, 0 | 0, 1, 0, 1, 1, 0
ここではシーケンスを分割しており、分割の前の値は常にゼロでなければなりません。そのようなシーケンスはいくつありますか? まあ、6 枚のカードに 3 人の勝者を含むシーケンスがあるのとまったく同じ数のシーケンスがあります。つまり 6 で 3 を選択します6! / (3! * 3!)
。
したがって、10 個の値のランダムな順列で、最初の 3 つの値に勝者が含まれない確率を取得するには、単純に次のように計算します。
(6 choose 3) / (10 choose 3)
逆オッズ (つまり、最初の 3 つのうち少なくとも 1 つに勝者が含まれるオッズ) を取得するには、次のようにします。
1 - (6 choose 3) / (10 choose 3)
total
= N
、winners
= M
、およびtries
=を使用した一般化Q
:
1 - ((N - Q) chose M) / (N chose M)
Python では、次のようになります。
>>> def choose(n, x):
... return reduce(mul, range(n, n - x, -1)) / math.factorial(x)
...
>>> def ntries_win_odds(total, winners, tries):
... inv = (choose(total - tries, winners) / float(choose(total, winners)))
... return 1 - inv
反対方向の解はそれほど難しくありません。与えられたとc = n choose x
を解く「逆選択」関数が必要なだけです。ここにはアルゴリズムの改善の余地があると思いますが、これはうまくいきます:n
c
x
>>> def choose_x_pseudoinverse(target, x):
... for n in itertools.count(start=x):
... if choose(n, x) >= target:
... return n
今、試行のために解決します:
odds = 1 - ((total - tries) chose winners) / (total chose winners)
(1 - odds) * (total choose winners) = ((total - tries) chose winners)
choose_x_inv((1 - odds) * (total choose winners), winners) = total - tries
tries = total - choose_x_inv((1 - odds) & (total choose winners), winners)
Pythonでは、それは
def ntries_from_odds(odds, total, winners):
inv_odds = 1 - odds
tCw = choose(total, winners)
return total - choose_x_pseudoinverse(inv_odds * tCw, winners)