combination function in python
Jussi Piitulainen
jpiitula at ling.helsinki.fi
Sun Apr 15 14:37:09 CEST 2007
Steven D'Aprano writes:
> bearophileHUGS wrote:
...
>> return factorial(n) // (factorial(k) * factorial(n-k))
>
> That's a naive and slow implementation. For even quite small values
> of n and k, you end up generating some seriously big long ints, and
> then have to (slowly!) divide them.
A better _definition_ of the binomial coefficient with upper index r
and lower index k is (r * (r - 1) * ...) / (k * (k - 1) * ...) with k
factors in both products. These are called falling factorial powers by
Graham, Knuth and Patashnik. Their notation is to write n^k and k^k
but with the exponent underlined; the latter is just k!, when k > 0. A
straightforward implementation below.
> A better implementation would be something like this:
>
> def binomial(n, k):
> if not 0 <= k <= n:
> return 0
> if k == 0 or k == n:
> return 1
> # calculate n!/k! as one product, avoiding factors that
> # just get canceled
> P = k+1
> for i in xrange(k+2, n+1):
> P *= i
> # if you are paranoid:
> # C, rem = divmod(P, factorial(n-k))
> # assert rem == 0
> # return C
> return P//factorial(n-k)
>
> There's probably even a really clever way to avoid that final
> division, but I suspect that would cost more in time and memory than
> it would save.
Here's one non-clever one for integers n, k that uses n^k / k^k
(falling powers) with the smaller of k and n - k as lower index:
def choose(n, k):
if 0 <= k <= n:
ntok = 1
ktok = 1
for t in xrange(1, min(k, n - k) + 1):
ntok *= n
ktok *= t
n -= 1
return ntok // ktok
else:
return 0
More information about the Python-list
mailing list