Binominal coefficient in python

925 Views Asked by At

so I was wondering how i could get around this problem where i have this code

    #defining a function that computes the factorial of an integer

def fac(b):
 if b==1:
    return 1
else:
    return b * fac(b-1)

#takes two integers and does the binomial coefficient operand

def combinations(n,k):
    result = (fac(n)) / (fac(k) * fac(n-k))
    return result

n=10
k=2

print(combinations(n,k))  

and using it to (nCk) large numbers such as (1000,700) without using the

 sys.setreccursionlimit(x)

for example. can i somehow remove the lowest values in (6C3) so it just calculates (6x5x4)/(3x2x1) since 6! is 6x5x4x3x2x1 and 3! is 3x2x1. and the formula is n!/(k!x(n-k)!)

and therefore lowering the amounts of recursions happening

3

There are 3 best solutions below

5
chc On

You can use the cache decorator (docs, nice video tutorial)

from functools import cache

@cache
def fac(b):
    return b * fac(b-1) if b else 1

def combinations(n,k):
    M = max(n,k)
    for i in range(M):
        fac(i)
    return (fac(n)) // (fac(k) * fac(n-k))


n=1000
k=700
print(combinations(n,k)) 

Output

542825004640614064815358503892902599588060075560435179852301016412253602009800031872232761420804306539976220810204913677796961128392686442868524741815732892024613137013599170443939815681313827516308854820419235457578544489551749630302863689773725905288736148678480
4
PApostol On

I feel you're kind of trying to reinvent the wheel here. This functionality already exists in the built-in math module, and it's quite efficient:

>>> import math
>>> math.comb(1000, 700)
542825004640614064815358503892902599588060075560435179852301016412253602009800031872232761420804306539976220810204913677796961128392686442868524741815732892024613137013599170443939815681313827516308854820419235457578544489551749630302863689773725905288736148678480
0
Stef On

Factorial

Recursion is great in some languages, but absolutely sucks in python. Do not use recursion in python unless you have a very good reason to.

For your factorial function, I suggest using a simple loop:

def factorial(n):
    result = 1
    for k in range(2,n+1):
        result *= k
    return result

Or you can use the prod function to compute the product of the range directly:

from math import prod

def factorial(n):
    return prod(range(2, n+1))

Or you can use the existing factorial function directly:

from math import factorial

Binomial coefficient

Mathematicians like to "compress" the formula of the binomial coefficient as (n choose k) = factorial(n) / (factorial(k) * factorial(n-k)), but this formula is inefficient for no good reason if used directly. Remember that all the factors in factorial(n-k) cancel out with the lower factors from factorial(n). So, there is no need to compute the product of these factors at all.

Instead you can at least do this small optimisation:

def binomial(n, k):
    a, b = (k, n-k) if k < n-k else (n-k, k)
    numerator = 1
    for i in range(b+1, n+1):
        numerator *= i
    return numerator / factorial(a)

Of course you can use the existing function comb directly:

from math import comb