从sciPy的甲板上绘制卡片与scipy.stats.hypergeom

时间:2012-07-01 23:34:29

标签: python scipy probability

我无法理解SciPy scipy.stats.hypergeom函数的the documentation。在我的程序中,我考虑各种卡片组并尝试找出各种抽奖的概率。 hypergeom类似乎具有此功能,但其文档假设我没有一堆术语知识。谷歌搜索带我到维基百科和沃尔夫勒姆MathWorld,两者都假设如果你问这种事情,你已经阅读了dang Principia Mathematica 的所有内容,只需要一点点复习 - 所以他们实际上并没有帮助。因为这个问题是“如何将这个特定的代码块应用于我的问题?”我问Stack Overflow。

我遇到的问题是“如果你有一副 N 卡片,其中 M 是感兴趣的卡片,有什么可能性顶部 Q 卡中至少有1张感兴趣的卡片?“我也有一个问题形式“如果你有一副 N 卡, M 是感兴趣的牌,你必须从牌组中抽出多少张牌他们中有一人有90%的机会成为感兴趣的卡片的复制品吗?“前一个问题非常接近SciPy文档中给出的示例问题,但它不是一回事,方法列表对我来说都是行话 - 我实际上无法分辨出哪一个是我需要的。我也无法分辨出哪种方法可用于后一类问题。

scipy.stats.hypergeom的方法实际上做了什么,他们的论点是什么,以及如何将它们应用于我的问题?假装我是一个中等聪明的高中生而不是数学博士候选人。

2 个答案:

答案 0 :(得分:4)

scipy.stats.hypergeom.pmf(k, M, n, N)

返回以下概率:来自M卡,其中n个被标记,如果您随机选择N卡而不进行替换,则将标记k卡。

所以你可以通过

得到你想要的答案(使用变量名)
def pick_Q(N, M, Q):
    """
    Given a deck of N cards, where M are marked,
    and Q cards are taken randomly without replacement,
    return the probability that at least one marked card is taken.
    """
    return sum(scipy.stats.hypergeom.pmf(k, N, M, Q) for k in xrange(1,Q+1))

(1张牌被标记的赔率,2张牌被标记,3张牌被标记...... N张牌被标记)。

幸运的是,有一种更快捷的方式 - 至少有一张标记牌的概率是没有选择标记牌的概率的另一面。所以你可以做

def pick_Q(N, M, Q):
    """
    Given a deck of N cards, where M are marked,
    and Q cards are taken randomly without replacement,
    return the probability that at least one marked card is taken.
    """
    return 1. - scipy.stats.hypergeom.pmf(0, N, M, Q)

对于你的第二个问题,似乎没有任何功能可以做你想要的;但是,你可以从

开始
def how_many_to_pick(N, M, prob):
    """
    Given a deck of N cards, M of which are marked,
    how many do you have to pick randomly without replacement
    to have at least prob probability of picking at least one marked card?
    """
    for q in xrange(1, M+1):
        if pick_Q(N, M, q) >= prob:
            return q
    raise ValueError("Could not find a value for q")

修改

scipy.stats.hypergeom.cdf(k, M, n, N)

给定一副M卡,其中n个被标记,随机选择N而不进行替换,找到 k或更少标记卡被挑选的几率。 (您可以将其视为.pmf的积分)

然后.sf(k,M,n,N)是.cdf的另一面 - 超过k 标记卡的几率被选中。

例如,

 k      pmf(k,52,13,4)   cdf(k,52,13,4)   sf(k,52,13,4)
     (exactly k picked)  ( <= k picked)   ( > k picked)
---  -----------------  ---------------  --------------
 0       0.303817527      0.303817527      0.696182473
 1       0.438847539      0.742665066      0.257334934
 2       0.213493397      0.956158463      0.043841537
 3       0.041200480      0.997358944      0.002641056
 4       0.002641056      1.000000000      0.000000000

<强> EDIT2:

实际上,这提供了另一种编写pick_Q功能的方法 - “挑选一张或多张标记的卡片”可以改为“挑选超过0张标记的卡片”,所以

def pick_Q(N, M, Q):
    """
    Given a deck of N cards, where M are marked,
    and Q cards are taken randomly without replacement,
    return the probability that at least one marked card is taken.
    """
    return scipy.stats.hypergeom.sf(0, N, M, Q)

答案 1 :(得分:2)

值得注意的是,如果不使用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 choose 310! / (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 = Nwinners = Mtries = 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给定nc的{​​{1}}。我觉得这里有算法改进的空间,但这很有效:

x

现在,解决尝试:

>>> def choose_x_pseudoinverse(target, x):
...     for n in itertools.count(start=x):
...         if choose(n, x) >= target:
...             return n

在Python中,那是

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)