Does Python have a function which computes multinomial coefficients?
To partially answer my own question, here is my simple and fairly efficient implementation of the multinomial function:
def multinomial(lst): res, i = 1, 1 for a in lst: for j in range(1,a+1): res *= i res //= j i += 1 return res
It seems from the comments so far that no efficient implementation of the function exists in any of the standard libraries.
Update (January 2020). As Don Hatch has pointed out in the comments, this can be further improved by looking for the largest argument (especially for the case that it dominates all others):
def multinomial(lst): res, i = 1, sum(lst) i0 = lst.index(max(lst)) for a in lst[:i0] + lst[i0+1:]: for j in range(1,a+1): res *= i res //= j i -= 1 return res
No, there is not a built-in multinomial library or function in Python.
Anyway this time math could help you. In fact a simple method for calculating the multinomial
keeping an eye on the performance is to rewrite it by using the characterization of the multinomial coefficient as a product of binomial coefficients:
where of course
Thanks to scipy.special.binom
and the magic of recursion you can solve the problem like this:
from scipy.special import binomdef multinomial(params): if len(params) == 1: return 1 return binom(sum(params), params[-1]) * multinomial(params[:-1])
where params = [n1, n2, ..., nk]
.
Note: Splitting the multinomial as a product of binomial is also good to prevent overflow in general.
You wrote "sympy.ntheory.multinomial.multinomial_coefficients
returns a dictionary related to multinomial coefficients", but it is not clear from that comment if you know how to extract the specific coefficients from that dictionary. Using the notation from the wikipedia link, the SymPy function gives you all the multinomial coefficients for the given m and n. If you only want a specific coefficient, just pull it out of the dictionary:
In [39]: from sympy import ntheoryIn [40]: def sympy_multinomial(params): ...: m = len(params) ...: n = sum(params) ...: return ntheory.multinomial_coefficients(m, n)[tuple(params)] ...: In [41]: sympy_multinomial([1, 2, 3])Out[41]: 60In [42]: sympy_multinomial([10, 20, 30])Out[42]: 3553261127084984957001360
Busy Beaver gave an answer written in terms of scipy.special.binom
. A potential problem with that implementation is that binom(n, k)
returns a floating point value. If the coefficient is large enough, it will not be exact, so it would probably not help you with a Project Euler problem. Instead of binom
, you can use scipy.special.comb
, with the argument exact=True
. This is Busy Beaver's function, modified to use comb
:
In [46]: from scipy.special import combIn [47]: def scipy_multinomial(params): ...: if len(params) == 1: ...: return 1 ...: coeff = (comb(sum(params), params[-1], exact=True) * ...: scipy_multinomial(params[:-1])) ...: return coeff ...: In [48]: scipy_multinomial([1, 2, 3])Out[48]: 60In [49]: scipy_multinomial([10, 20, 30])Out[49]: 3553261127084984957001360