It is worth noting that this is not difficult to solve without using scipy at all. Let's say we have a random permutation of 10 elements:
4, 7, 2, 3, 0, 9, 1, 5, 6, 8
And a set of "winners" 2, 4, 6 . Since all we care about are winners and losers, we can simplify our view a bit:
1, 0, 1, 0, 0, 0, 0, 0, 1, 0
We can do the same with any set of 10 possible items and 3 winners; and with any possible permutation of 10 points, we can perform the same simplification. So what actually happens is that each permutation “selects” 3 winning indexes, and the number of possible arrangements of the winners in deck 10 selects 3 , or 10! / (3! * 7!) 10! / (3! * 7!) .
Now we need to know how many of those possible winners give us at least one winner in the first Q cards. Since it’s easier to calculate how many arrangements give us exactly zero winners in the first Q cards, we will calculate this instead. So we want, in the most specific terms, the number of sequences that look like this (for Q = 4):
0, 0, 0, 0 | 0, 1, 0, 1, 1, 0
Here we divided the sequence, and the values preceding the section should always be zero. How many such sequences exist? Well, such sequences are exactly the same as sequences containing 3 winners in 6 cards. So 6 select 3, i.e. 6! / (3! * 3!) 6! / (3! * 3!) .
So, to get the chances that with a random permutation of 10 values, the first three do not contain a winner, we simply calculate the following:
(6 choose 3) / (10 choose 3)
And to get the inverse coefficients (i.e. the probability that at least one of the first three contains a winner), we do the following:
1 - (6 choose 3) / (10 choose 3)
Summarizing, with total = N , winners = M and tries = Q :
1 - ((N - Q) chose M) / (N chose M)
In python, it looks like this:
>>> 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
Deciding in the opposite direction is not too complicated - we just need a "reverse selection" function that solves c = n choose x for N given c and x . Where I want, there is an algorithmic improvement, but this works:
>>> def choose_x_pseudoinverse(target, x): ... for n in itertools.count(start=x): ... if choose(n, x) >= target: ... return n
Now the solution for trying:
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)
In Python, this is
def ntries_from_odds(odds, total, winners): inv_odds = 1 - odds tCw = choose(total, winners) return total - choose_x_pseudoinverse(inv_odds * tCw, winners)