Related
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed last month.
I've written a simple implementation of the trapezoidal method to find the integral of sine:
def trapezoidal_method(a: float, b: float, n: int) -> float:
length = (b - a)/n
integral = 0
start = a
integral += math.sin(a)/2
for _ in range(1, n):
integral += math.sin(start + length)
start += length
integral += math.sin(b)/2
return integral * length
It converges as expected for most situations...
but it goes crazy when the result should be 0 (like integrating from -1 to 1):
How do I fix this? Tried explicitly casting n to float and tried using the decimal library, didn't change anything
That's (mainly) due to floating number precision. In the case of functions as sine, which are transcendental by nature, only an approximation of it is possible (or depends on its implementation).
By adding a (very quick!) a print of the x and y values at each term of the sequence you will see the odd symmetry is not respected... or at least with a small error. So, f(x) = -f(-x) + error.
import math
def trapezoidal_method(func, a: float, b: float, n: int) -> float:
length = (b - a)/n
integral = 0
start = a
integral += func(a)/2
for _ in range(1, n):
integral += func(start + length)
start += length
integral += func(b)/2
# evaluation for each term of the sequence
print(0, f'x={a} y={func(a)}')
x = a
for i in range(1, n):
x += length
y = func(x)
print(i, f'{x=} {y=}')
print(n, f'x={b} y={func(b)}')
return integral * length
A test with sin
a, b, n = -1, 1, 7
func = math.sin
tm = trapezoidal_method(func, a, b, n)
Output
0 x=-1 y=-0.8414709848078965
1 x=-0.7142857142857143 y=-0.6550778971785186
2 x=-0.4285714285714286 y=-0.41557185499305205 # <-
3 x=-0.1428571428571429 y=-0.1423717297922637
4 x= 0.1428571428571428 y=0.1423717297922636
5 x= 0.4285714285714285 y=0.41557185499305194 # <-
6 x= 0.7142857142857142 y=0.6550778971785185
8 x= 1 y=0.8414709848078965
-1.586032892321652e-16
Notice that also the x-values maybe slightly different!
By choosing suitable limits of integration and amount of strips then the in some cases the integral maybe 0. Here an example with the diagonal function
a, b, n = -1, 1, 6
func = lambda x: x
tm = trapezoidal_method(func, a, b, n)
Output
0 x=-3.0 y=-3.0
1 x=-2.0 y=-2.0
2 x=-1.0 y=-1.0
3 x=0.0 y=0.0
4 x=1.0 y=1.0
5 x=2.0 y=2.0
7 x=3.0 y=3.0
0.0
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == 1 or y == x:
print(1)
if y > x:
print(0)
else:
a = math.factorial(x)
b = math.factorial(y)
div = a // (b*(x-y))
print(div)
This binomial coefficient program works but when I input two of the same number which is supposed to equal to 1 or when y is greater than x it is supposed to equal to 0.
This question is old but as it comes up high on search results I will point out that scipy has two functions for computing the binomial coefficients:
scipy.special.binom()
scipy.special.comb()
import scipy.special
# the two give the same results
scipy.special.binom(10, 5)
# 252.0
scipy.special.comb(10, 5)
# 252.0
scipy.special.binom(300, 150)
# 9.375970277281882e+88
scipy.special.comb(300, 150)
# 9.375970277281882e+88
# ...but with `exact == True`
scipy.special.comb(10, 5, exact=True)
# 252
scipy.special.comb(300, 150, exact=True)
# 393759702772827452793193754439064084879232655700081358920472352712975170021839591675861424
Note that scipy.special.comb(exact=True) uses Python integers, and therefore it can handle arbitrarily large results!
Speed-wise, the three versions give somewhat different results:
num = 300
%timeit [[scipy.special.binom(n, k) for k in range(n + 1)] for n in range(num)]
# 52.9 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit [[scipy.special.comb(n, k) for k in range(n + 1)] for n in range(num)]
# 183 ms ± 814 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)each)
%timeit [[scipy.special.comb(n, k, exact=True) for k in range(n + 1)] for n in range(num)]
# 180 ms ± 649 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
(and for n = 300, the binomial coefficients are too large to be represented correctly using float64 numbers, as shown above).
Note that starting Python 3.8, the standard library provides the math.comb function to compute the binomial coefficient:
math.comb(n, k)
which is the number of ways to choose k items from n items without repetition n! / (k! (n - k)!):
import math
math.comb(10, 5) # 252
math.comb(10, 10) # 1
Here's a version that actually uses the correct formula . :)
#! /usr/bin/env python
''' Calculate binomial coefficient xCy = x! / (y! (x-y)!)
'''
from math import factorial as fac
def binomial(x, y):
try:
return fac(x) // fac(y) // fac(x - y)
except ValueError:
return 0
#Print Pascal's triangle to test binomial()
def pascal(m):
for x in range(m + 1):
print([binomial(x, y) for y in range(x + 1)])
def main():
#input = raw_input
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
print(binomial(x, y))
if __name__ == '__main__':
#pascal(8)
main()
...
Here's an alternate version of binomial() I wrote several years ago that doesn't use math.factorial(), which didn't exist in old versions of Python. However, it returns 1 if r is not in range(0, n+1).
def binomial(n, r):
''' Binomial coefficient, nCr, aka the "choose" function
n! / (r! * (n - r)!)
'''
p = 1
for i in range(1, min(r, n - r) + 1):
p *= n
p //= i
n -= 1
return p
So, this question comes up first if you search for "Implement binomial coefficients in Python". Only this answer in its second part contains an efficient implementation which relies on the multiplicative formula. This formula performs the bare minimum number of multiplications. The function below does not depend on any built-ins or imports:
def fcomb0(n, k):
'''
Compute the number of ways to choose $k$ elements out of a pile of $n.$
Use an iterative approach with the multiplicative formula:
$$\frac{n!}{k!(n - k)!} =
\frac{n(n - 1)\dots(n - k + 1)}{k(k-1)\dots(1)} =
\prod_{i = 1}^{k}\frac{n + 1 - i}{i}$$
Also rely on the symmetry: $C_n^k = C_n^{n - k},$ so the product can
be calculated up to $\min(k, n - k).$
:param n: the size of the pile of elements
:param k: the number of elements to take from the pile
:return: the number of ways to choose k elements out of a pile of n
'''
# When k out of sensible range, should probably throw an exception.
# For compatibility with scipy.special.{comb, binom} returns 0 instead.
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
total_ways = 1
for i in range(min(k, n - k)):
total_ways = total_ways * (n - i) // (i + 1)
return total_ways
Finally, if you need even larger values and do not mind trading some accuracy, Stirling's approximation is probably the way to go.
Your program will continue with the second if statement in the case of y == x, causing a ZeroDivisionError. You need to make the statements mutually exclusive; the way to do that is to use elif ("else if") instead of if:
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == x:
print(1)
elif y == 1: # see georg's comment
print(x)
elif y > x: # will be executed only if y != 1 and y != x
print(0)
else: # will be executed only if y != 1 and y != x and x <= y
a = math.factorial(x)
b = math.factorial(y)
c = math.factorial(x-y) # that appears to be useful to get the correct result
div = a // (b * c)
print(div)
What about this one? :) It uses correct formula, avoids math.factorial and takes less multiplication operations:
import math
import operator
product = lambda m,n: reduce(operator.mul, xrange(m, n+1), 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(y+1, x) / product(1, x-y)
Also, in order to avoid big-integer arithmetics you may use floating point numbers, convert
product(a[i])/product(b[i]) to product(a[i]/b[i]) and rewrite the above program as:
import math
import operator
product = lambda iterable: reduce(operator.mul, iterable, 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(map(operator.truediv, xrange(y+1, x+1), xrange(1, x-y+1)))
For Python 3, scipy has the function scipy.special.comb, which may produce floating point as well as exact integer results
import scipy.special
res = scipy.special.comb(x, y, exact=True)
See the documentation for scipy.special.comb.
For Python 2, the function is located in scipy.misc, and it works the same way:
import scipy.misc
res = scipy.misc.comb(x, y, exact=True)
I recommend using dynamic programming (DP) for computing binomial coefficients. In contrast to direct computation, it avoids multiplication and division of large numbers. In addition to recursive solution, it stores previously solved overlapping sub-problems in a table for fast look-up. The code below shows bottom-up (tabular) DP and top-down (memoized) DP implementations for computing binomial coefficients.
def binomial_coeffs1(n, k):
#top down DP
if (k == 0 or k == n):
return 1
if (memo[n][k] != -1):
return memo[n][k]
memo[n][k] = binomial_coeffs1(n-1, k-1) + binomial_coeffs1(n-1, k)
return memo[n][k]
def binomial_coeffs2(n, k):
#bottom up DP
for i in range(n+1):
for j in range(min(i,k)+1):
if (j == 0 or j == i):
memo[i][j] = 1
else:
memo[i][j] = memo[i-1][j-1] + memo[i-1][j]
#end if
#end for
#end for
return memo[n][k]
def print_array(memo):
for i in range(len(memo)):
print('\t'.join([str(x) for x in memo[i]]))
#main
n = 5
k = 2
print("top down DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs1(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
print("bottom up DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs2(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
Note: the size of the memo table is set to a small value (6) for display purposes, it should be increased if you are computing binomial coefficients for large n and k.
Here is a function that recursively calculates the binomial coefficients using conditional expressions
def binomial(n,k):
return 1 if k==0 else (0 if n==0 else binomial(n-1, k) + binomial(n-1, k-1))
It's a good idea to apply a recursive definition, as in Vadim Smolyakov's answer, combined with a DP (dynamic programming), but for the latter you may apply the lru_cache decorator from module functools:
import functools
#functools.lru_cache(maxsize = None)
def binom(n,k):
if k == 0: return 1
if n == k: return 1
return binom(n-1,k-1)+binom(n-1,k)
The simplest way is using the Multiplicative formula. It works for (n,n) and (n,0) as expected.
def coefficient(n,k):
c = 1.0
for i in range(1, k+1):
c *= float((n+1-i))/float(i)
return c
Multiplicative formula
A bit shortened multiplicative variant given by PM 2Ring and alisianoi. Works with python 3 and doesn't require any packages.
def comb(n, k):
# Remove the next two lines if out-of-range check is not needed
if k < 0 or k > n:
return None
x = 1
for i in range(min(k, n - k)):
x = x*(n - i)//(i + 1)
return x
Or
from functools import reduce
def comb(n, k):
return (None if k < 0 or k > n else
reduce(lambda x, i: x*(n - i)//(i + 1), range(min(k, n - k)), 1))
The division is done right after multiplication not to accumulate high numbers.
For everyone looking for the log of the binomial coefficient (Theano calls this binomln), this answer has it:
from numpy import log
from scipy.special import betaln
def binomln(n, k):
"Log of scipy.special.binom calculated entirely in the log domain"
return -betaln(1 + n - k, 1 + k) - log(n + 1)
(And if your language/library lacks betaln but has gammaln, like Go, have no fear, since betaln(a, b) is just gammaln(a) + gammaln(b) - gammaln(a + b), per MathWorld.)
import math
def binomial_coefficients(n,k):
product = 1
for i in range(k):
product = math.floor(((product * (n - i))/(i + 1))
return product
in the calculation of the binomial coefficients, you should not calculate the finite product n(n-1) ... (n - k +1) for (n, k) and k!. This could cause an overflow error. Therefore, using a bit of number theory we can assume that the inputs will always be in integer form (since the combination of (n, k) only accepts integers)) we can see that for an integer 'i' in a product of consecutive integers, any term u in the product will always be divisible by i.
NOTE: you can do this without importing the math module. math.floor(a/b) is equivalent to a // b
I am writing a program that handles numbers as large as 10 ** 100, everything looks good when dealing with smaller numbers but when values get big I get these kind of problems:
>>> N = 615839386751705599129552248800733595745824820450766179696019084949158872160074326065170966642688
>>> ((N + 63453534345) / sqrt(2)) == (N / sqrt(2))
>>> True
Clearly the above comparision is false, why is this happening?
Program code:
from math import *
def rec (n):
r = sqrt (2)
s = r + 2
m = int (floor (n * r))
j = int (floor (m / s))
if j <= 1:
return sum ([floor (r * i) for i in range (1, n + 1)])
assert m >= s * j and j > 1, "Error: something went wrong"
return m * (m + 1) / 2 - j * (j + 1) - rec (j)
print rec (1e100)
Edit:
I don't think my question is a duplicate of the linked question above because the decimal points in n, m and j are not important to me and I am looking for a solution to avoid this precision issue.
You can’t retain the precision you want while dividing by standard floating point numbers, so you should instead divide by a Fraction. The Fraction class in the fractions module lets you do exact rational arithmetic.
Of course, the square root of 2 is not rational. But if the error is less than one part in 10**100, you’ll get the right result.
So, how to compute an approximation to sqrt(2) as a Fraction? There are several ways to do it, but one simple way is to compute the integer square root of 2 * 10**200, which will be close to sqrt(2) * 10**100, then just make that the numerator and make 10**100 the denominator.
Here’s a little routine in Python 3 for integer square root.
def isqrt(n):
lg = -1
g = (1 >> n.bit_length() // 2) + 1
while abs(lg - g) > 1:
lg = g
g = (g + n//g) // 2
while g * g > n:
g -= 1
return g
You should be able to take it from there.
I have a very large number, both before and after the decimal, but for this I'll just call it 4.58.
I want to know the number, Y, that will yield me an integer if multiplied by X and not any sort of float number.
Here is my code:
from decimal import *
setcontext(ExtendedContext)
getcontext().prec = 300
x=Decimal('4.58')
while True:
i=1
a=Decimal(i*x)
if float(a).is_integer():
print(i*x)
break
else:
i=+1
However, this method is incredibly slow and inefficient. I was wondering how could I implement continued fractions or some other method to make it predict the value of Y?
Edit
The decimal module stores float numbers more accurately (As strings), so 0.5 won't become 0.499999999.
Edit 2
I've got X (4.58).
I want to know what number will multiply by X to make an integer; as efficiently as possible.
Edit 3
Okay, maybe not my best question yet.
Here's my dilemma.
I've got a number spat out from a trivial programme I made. That number is a decimal number, 1.5.
All I want to do is find what integer will multiply by my decimal to yield another integer.
For 1.5, the best answer will be 2. (1.5*2=3) (float*int=int)
My while-loop above will do that, eventually, but I just wanted to know whether there was a better way to do this, such as continued fractions; and if there was, how could I implement it.
Edit 4
Here's my code thanks to user6794072. It's lengthy but functional.
from gmpy2 import mpz, isqrt
from fractions import Fraction
import operator
import functools
from decimal import *
setcontext(ExtendedContext)
getcontext().prec = 300
def factors(n):
n = mpz(n)
result = set()
result |= {mpz(1), n}
def all_multiples(result, n, factor):
z = n
f = mpz(factor)
while z % f == 0:
result |= {f, z // f}
f += factor
return result
result = all_multiples(result, n, 2)
result = all_multiples(result, n, 3)
for i in range(1, isqrt(n) + 1, 6):
i1 = i + 1
i2 = i + 5
if not n % i1:
result |= {mpz(i1), n // i1}
if not n % i2:
result |= {mpz(i2), n // i2}
return result
j=Decimal('4.58')
a=(Fraction(j).numerator)
b=(Fraction(j).denominator)
y=(factors(a))
x=(factors(b))
q=([item for item in x if item not in y])
w=([item for item in y if item not in x]) q.extend(w)
p=(functools.reduce(operator.mul, q, 1)) ans=(p*j)
print(ans)
If I understand your question correctly, you want to find the smallest integer (i) that can be multiplied to a non-integer number (n) so that:
i*n is an integer
I would do this by finding the factors of the numerator and denominator for n. In your example, if n = 4.58, then you can extract 458 for the numerator and 100 for the denominator.
The multiples of 458 are 2 and 229
The multiples of 100 are 2, 2, 5, 5
You can cross off one instance of 2 for the numerator and denominator. Then your solution is just multiplying the remaining factors in the denominator: in this case, 2*5*5 or 50.
Well think of what if you wanted to reach z = 1 and then use the fact that z == z * 1 to scale the answer. For any float x != 0.0, y = 1/x will yield z = 1, so for arbitrary integer z, just use y = z/x.
I'm not a Python programmer, but what about round function?
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == 1 or y == x:
print(1)
if y > x:
print(0)
else:
a = math.factorial(x)
b = math.factorial(y)
div = a // (b*(x-y))
print(div)
This binomial coefficient program works but when I input two of the same number which is supposed to equal to 1 or when y is greater than x it is supposed to equal to 0.
This question is old but as it comes up high on search results I will point out that scipy has two functions for computing the binomial coefficients:
scipy.special.binom()
scipy.special.comb()
import scipy.special
# the two give the same results
scipy.special.binom(10, 5)
# 252.0
scipy.special.comb(10, 5)
# 252.0
scipy.special.binom(300, 150)
# 9.375970277281882e+88
scipy.special.comb(300, 150)
# 9.375970277281882e+88
# ...but with `exact == True`
scipy.special.comb(10, 5, exact=True)
# 252
scipy.special.comb(300, 150, exact=True)
# 393759702772827452793193754439064084879232655700081358920472352712975170021839591675861424
Note that scipy.special.comb(exact=True) uses Python integers, and therefore it can handle arbitrarily large results!
Speed-wise, the three versions give somewhat different results:
num = 300
%timeit [[scipy.special.binom(n, k) for k in range(n + 1)] for n in range(num)]
# 52.9 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit [[scipy.special.comb(n, k) for k in range(n + 1)] for n in range(num)]
# 183 ms ± 814 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)each)
%timeit [[scipy.special.comb(n, k, exact=True) for k in range(n + 1)] for n in range(num)]
# 180 ms ± 649 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
(and for n = 300, the binomial coefficients are too large to be represented correctly using float64 numbers, as shown above).
Note that starting Python 3.8, the standard library provides the math.comb function to compute the binomial coefficient:
math.comb(n, k)
which is the number of ways to choose k items from n items without repetition n! / (k! (n - k)!):
import math
math.comb(10, 5) # 252
math.comb(10, 10) # 1
Here's a version that actually uses the correct formula . :)
#! /usr/bin/env python
''' Calculate binomial coefficient xCy = x! / (y! (x-y)!)
'''
from math import factorial as fac
def binomial(x, y):
try:
return fac(x) // fac(y) // fac(x - y)
except ValueError:
return 0
#Print Pascal's triangle to test binomial()
def pascal(m):
for x in range(m + 1):
print([binomial(x, y) for y in range(x + 1)])
def main():
#input = raw_input
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
print(binomial(x, y))
if __name__ == '__main__':
#pascal(8)
main()
...
Here's an alternate version of binomial() I wrote several years ago that doesn't use math.factorial(), which didn't exist in old versions of Python. However, it returns 1 if r is not in range(0, n+1).
def binomial(n, r):
''' Binomial coefficient, nCr, aka the "choose" function
n! / (r! * (n - r)!)
'''
p = 1
for i in range(1, min(r, n - r) + 1):
p *= n
p //= i
n -= 1
return p
So, this question comes up first if you search for "Implement binomial coefficients in Python". Only this answer in its second part contains an efficient implementation which relies on the multiplicative formula. This formula performs the bare minimum number of multiplications. The function below does not depend on any built-ins or imports:
def fcomb0(n, k):
'''
Compute the number of ways to choose $k$ elements out of a pile of $n.$
Use an iterative approach with the multiplicative formula:
$$\frac{n!}{k!(n - k)!} =
\frac{n(n - 1)\dots(n - k + 1)}{k(k-1)\dots(1)} =
\prod_{i = 1}^{k}\frac{n + 1 - i}{i}$$
Also rely on the symmetry: $C_n^k = C_n^{n - k},$ so the product can
be calculated up to $\min(k, n - k).$
:param n: the size of the pile of elements
:param k: the number of elements to take from the pile
:return: the number of ways to choose k elements out of a pile of n
'''
# When k out of sensible range, should probably throw an exception.
# For compatibility with scipy.special.{comb, binom} returns 0 instead.
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
total_ways = 1
for i in range(min(k, n - k)):
total_ways = total_ways * (n - i) // (i + 1)
return total_ways
Finally, if you need even larger values and do not mind trading some accuracy, Stirling's approximation is probably the way to go.
Your program will continue with the second if statement in the case of y == x, causing a ZeroDivisionError. You need to make the statements mutually exclusive; the way to do that is to use elif ("else if") instead of if:
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == x:
print(1)
elif y == 1: # see georg's comment
print(x)
elif y > x: # will be executed only if y != 1 and y != x
print(0)
else: # will be executed only if y != 1 and y != x and x <= y
a = math.factorial(x)
b = math.factorial(y)
c = math.factorial(x-y) # that appears to be useful to get the correct result
div = a // (b * c)
print(div)
What about this one? :) It uses correct formula, avoids math.factorial and takes less multiplication operations:
import math
import operator
product = lambda m,n: reduce(operator.mul, xrange(m, n+1), 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(y+1, x) / product(1, x-y)
Also, in order to avoid big-integer arithmetics you may use floating point numbers, convert
product(a[i])/product(b[i]) to product(a[i]/b[i]) and rewrite the above program as:
import math
import operator
product = lambda iterable: reduce(operator.mul, iterable, 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(map(operator.truediv, xrange(y+1, x+1), xrange(1, x-y+1)))
For Python 3, scipy has the function scipy.special.comb, which may produce floating point as well as exact integer results
import scipy.special
res = scipy.special.comb(x, y, exact=True)
See the documentation for scipy.special.comb.
For Python 2, the function is located in scipy.misc, and it works the same way:
import scipy.misc
res = scipy.misc.comb(x, y, exact=True)
I recommend using dynamic programming (DP) for computing binomial coefficients. In contrast to direct computation, it avoids multiplication and division of large numbers. In addition to recursive solution, it stores previously solved overlapping sub-problems in a table for fast look-up. The code below shows bottom-up (tabular) DP and top-down (memoized) DP implementations for computing binomial coefficients.
def binomial_coeffs1(n, k):
#top down DP
if (k == 0 or k == n):
return 1
if (memo[n][k] != -1):
return memo[n][k]
memo[n][k] = binomial_coeffs1(n-1, k-1) + binomial_coeffs1(n-1, k)
return memo[n][k]
def binomial_coeffs2(n, k):
#bottom up DP
for i in range(n+1):
for j in range(min(i,k)+1):
if (j == 0 or j == i):
memo[i][j] = 1
else:
memo[i][j] = memo[i-1][j-1] + memo[i-1][j]
#end if
#end for
#end for
return memo[n][k]
def print_array(memo):
for i in range(len(memo)):
print('\t'.join([str(x) for x in memo[i]]))
#main
n = 5
k = 2
print("top down DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs1(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
print("bottom up DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs2(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
Note: the size of the memo table is set to a small value (6) for display purposes, it should be increased if you are computing binomial coefficients for large n and k.
Here is a function that recursively calculates the binomial coefficients using conditional expressions
def binomial(n,k):
return 1 if k==0 else (0 if n==0 else binomial(n-1, k) + binomial(n-1, k-1))
It's a good idea to apply a recursive definition, as in Vadim Smolyakov's answer, combined with a DP (dynamic programming), but for the latter you may apply the lru_cache decorator from module functools:
import functools
#functools.lru_cache(maxsize = None)
def binom(n,k):
if k == 0: return 1
if n == k: return 1
return binom(n-1,k-1)+binom(n-1,k)
The simplest way is using the Multiplicative formula. It works for (n,n) and (n,0) as expected.
def coefficient(n,k):
c = 1.0
for i in range(1, k+1):
c *= float((n+1-i))/float(i)
return c
Multiplicative formula
A bit shortened multiplicative variant given by PM 2Ring and alisianoi. Works with python 3 and doesn't require any packages.
def comb(n, k):
# Remove the next two lines if out-of-range check is not needed
if k < 0 or k > n:
return None
x = 1
for i in range(min(k, n - k)):
x = x*(n - i)//(i + 1)
return x
Or
from functools import reduce
def comb(n, k):
return (None if k < 0 or k > n else
reduce(lambda x, i: x*(n - i)//(i + 1), range(min(k, n - k)), 1))
The division is done right after multiplication not to accumulate high numbers.
For everyone looking for the log of the binomial coefficient (Theano calls this binomln), this answer has it:
from numpy import log
from scipy.special import betaln
def binomln(n, k):
"Log of scipy.special.binom calculated entirely in the log domain"
return -betaln(1 + n - k, 1 + k) - log(n + 1)
(And if your language/library lacks betaln but has gammaln, like Go, have no fear, since betaln(a, b) is just gammaln(a) + gammaln(b) - gammaln(a + b), per MathWorld.)
import math
def binomial_coefficients(n,k):
product = 1
for i in range(k):
product = math.floor(((product * (n - i))/(i + 1))
return product
in the calculation of the binomial coefficients, you should not calculate the finite product n(n-1) ... (n - k +1) for (n, k) and k!. This could cause an overflow error. Therefore, using a bit of number theory we can assume that the inputs will always be in integer form (since the combination of (n, k) only accepts integers)) we can see that for an integer 'i' in a product of consecutive integers, any term u in the product will always be divisible by i.
NOTE: you can do this without importing the math module. math.floor(a/b) is equivalent to a // b