值得注意的是,这不是一个不使用就很难解决的问题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 选择 3,或者10! / (3! * 7!)
。
现在我们需要知道的是,有多少可能的获胜者安排在第一Q
张牌中至少给了我们一个获胜者。由于更容易计算有多少安排在第一张牌中给了我们零Q
赢家,所以我们将计算它。所以我们想要的,用最具体的术语来说,是看起来像这样的序列的数量(对于 Q = 4):
0, 0, 0, 0 | 0, 1, 0, 1, 1, 0
在这里,我们已经对序列进行了分区,分区之前的值应该始终为零。有多少这样的序列?嗯,这样的序列与包含 6 张牌中 3 位获胜者的序列一样多。所以这是 6 选择 3,即6! / (3! * 3!)
。
因此,要获得在 10 个值的随机排列中,前三个不包含获胜者的几率,我们只需计算以下内容:
(6 choose 3) / (10 choose 3)
为了获得相反的赔率(即前三个中至少一个包含获胜者的赔率),我们这样做:
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)