Combinatorial
This module implements various combinatorial functions.
bell
- class diofant.functions.combinatorial.numbers.bell(n, k_sym=None, symbols=None)[source]
Bell numbers / Bell polynomials
The Bell numbers satisfy \(B_0 = 1\) and
\[B_n = \sum_{k=0}^{n-1} \binom{n-1}{k} B_k.\]They are also given by:
\[B_n = \frac{1}{e} \sum_{k=0}^{\infty} \frac{k^n}{k!}.\]The Bell polynomials are given by \(B_0(x) = 1\) and
\[B_n(x) = x \sum_{k=1}^{n-1} \binom{n-1}{k-1} B_{k-1}(x).\]The second kind of Bell polynomials (are sometimes called “partial” Bell polynomials or incomplete Bell polynomials) are defined as
\[B_{n,k}(x_1, x_2,\dotsc x_{n-k+1}) = \sum_{j_1+j_2+j_2+\dotsb=k \atop j_1+2j_2+3j_2+\dotsb=n} \frac{n!}{j_1!j_2!\dotsb j_{n-k+1}!} \left(\frac{x_1}{1!} \right)^{j_1} \left(\frac{x_2}{2!} \right)^{j_2} \dotsb \left(\frac{x_{n-k+1}}{(n-k+1)!} \right) ^{j_{n-k+1}}.\]bell(n) gives the \(n^{th}\) Bell number, \(B_n\).
bell(n, x) gives the \(n^{th}\) Bell polynomial, \(B_n(x)\).
bell(n, k, (x1, x2, …)) gives Bell polynomials of the second kind, \(B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})\).
Notes
Not to be confused with Bernoulli numbers and Bernoulli polynomials, which use the same notation.
Examples
>>> [bell(n) for n in range(11)] [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975] >>> bell(30) 846749014511809332450147 >>> bell(4, Symbol('t')) t**4 + 6*t**3 + 7*t**2 + t >>> bell(6, 2, symbols('x:6')[1:]) 6*x1*x5 + 15*x2*x4 + 10*x3**2
References
See also
diofant.functions.combinatorial.numbers.bernoulli
,diofant.functions.combinatorial.numbers.catalan
,diofant.functions.combinatorial.numbers.euler
,diofant.functions.combinatorial.numbers.fibonacci
,diofant.functions.combinatorial.numbers.harmonic
,diofant.functions.combinatorial.numbers.lucas
bernoulli
- class diofant.functions.combinatorial.numbers.bernoulli(n, sym=None)[source]
Bernoulli numbers / Bernoulli polynomials
The Bernoulli numbers are a sequence of rational numbers defined by B_0 = 1 and the recursive relation (n > 0):
n ___ \ / n + 1 \ 0 = ) | | * B . /___ \ k / k k = 0
They are also commonly defined by their exponential generating function, which is x/(exp(x) - 1). For odd indices > 1, the Bernoulli numbers are zero.
The Bernoulli polynomials satisfy the analogous formula:
n ___ \ / n \ n-k B (x) = ) | | * B * x . n /___ \ k / k k = 0
Bernoulli numbers and Bernoulli polynomials are related as B_n(0) = B_n.
We compute Bernoulli numbers using Ramanujan’s formula:
/ n + 3 \ B = (A(n) - S(n)) / | | n \ n /
where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 when n = 4 (mod 6), and:
[n/6] ___ \ / n + 3 \ S(n) = ) | | * B /___ \ n - 6*k / n-6*k k = 1
This formula is similar to the sum given in the definition, but cuts 2/3 of the terms. For Bernoulli polynomials, we use the formula in the definition.
bernoulli(n) gives the nth Bernoulli number, B_n
bernoulli(n, x) gives the nth Bernoulli polynomial in x, B_n(x)
Examples
>>> [bernoulli(n) for n in range(11)] [1, -1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66] >>> bernoulli(1000001) 0
References
binomial
- class diofant.functions.combinatorial.factorials.binomial(n, k)[source]
Implementation of the binomial coefficient. It can be defined in two ways depending on its desired interpretation:
C(n,k) = n!/(k!(n-k)!) or C(n, k) = ff(n, k)/k!
First, in a strict combinatorial sense it defines the number of ways we can choose ‘k’ elements from a set of ‘n’ elements. In this case both arguments are nonnegative integers and binomial is computed using an efficient algorithm based on prime factorization.
The other definition is generalization for arbitrary ‘n’, however ‘k’ must also be nonnegative. This case is very useful when evaluating summations.
For the sake of convenience for negative ‘k’ this function will return zero no matter what valued is the other argument.
To expand the binomial when n is a symbol, use either expand_func() or expand(func=True). The former will keep the polynomial in factored form while the latter will expand the polynomial itself. See examples for details.
Examples
>>> n = Symbol('n', integer=True, positive=True)
>>> binomial(15, 8) 6435
>>> binomial(n, -1) 0
Rows of Pascal’s triangle can be generated with the binomial function:
>>> for N in range(8): ... [binomial(N, i) for i in range(N + 1)] ... [1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] [1, 5, 10, 10, 5, 1] [1, 6, 15, 20, 15, 6, 1] [1, 7, 21, 35, 35, 21, 7, 1]
As can a given diagonal, e.g. the 4th diagonal:
>>> N = -4 >>> [binomial(N, i) for i in range(1 - N)] [1, -4, 10, -20, 35]
>>> binomial(n, 3) binomial(n, 3)
>>> binomial(n, 3).expand(func=True) n**3/6 - n**2/2 + n/3
>>> expand_func(binomial(n, 3)) n*(n - 2)*(n - 1)/6
catalan
- class diofant.functions.combinatorial.numbers.catalan(n)[source]
Catalan numbers
The n-th catalan number is given by:
1 / 2*n \ C = ----- | | n n + 1 \ n /
catalan(n) gives the n-th Catalan number, C_n
Examples
>>> [catalan(i) for i in range(1, 10)] [1, 2, 5, 14, 42, 132, 429, 1430, 4862]
>>> catalan(n) catalan(n)
Catalan numbers can be transformed into several other, identical expressions involving other mathematical functions
>>> catalan(n).rewrite(binomial) binomial(2*n, n)/(n + 1)
>>> catalan(n).rewrite(gamma) 4**n*gamma(n + 1/2)/(sqrt(pi)*gamma(n + 2))
>>> catalan(n).rewrite(hyper) hyper((-n + 1, -n), (2,), 1)
For some non-integer values of n we can get closed form expressions by rewriting in terms of gamma functions:
>>> catalan(Rational(1, 2)).rewrite(gamma) 8/(3*pi)
We can differentiate the Catalan numbers C(n) interpreted as a continuous real function in n:
>>> diff(catalan(n), n) (polygamma(0, n + 1/2) - polygamma(0, n + 2) + log(4))*catalan(n)
As a more advanced example consider the following ratio between consecutive numbers:
>>> combsimp((catalan(n + 1)/catalan(n)).rewrite(binomial)) 2*(2*n + 1)/(n + 2)
The Catalan numbers can be generalized to complex numbers:
>>> catalan(I).rewrite(gamma) 4**I*gamma(1/2 + I)/(sqrt(pi)*gamma(2 + I))
and evaluated with arbitrary precision:
>>> catalan(I).evalf(20) 0.39764993382373624267 - 0.020884341620842555705*I
References
See also
diofant.functions.combinatorial.numbers.bell
,diofant.functions.combinatorial.numbers.bernoulli
,diofant.functions.combinatorial.numbers.euler
,diofant.functions.combinatorial.numbers.fibonacci
,diofant.functions.combinatorial.numbers.harmonic
,diofant.functions.combinatorial.numbers.lucas
,diofant.functions.combinatorial.factorials.binomial
euler
- class diofant.functions.combinatorial.numbers.euler(m)[source]
Euler numbers
The euler numbers are given by:
2*n+1 k ___ ___ j 2*n+1 \ \ / k \ (-1) * (k-2*j) E = I ) ) | | -------------------- 2n /___ /___ \ j / k k k = 1 j = 0 2 * I * k E = 0 2n+1
euler(n) gives the n-th Euler number, E_n
Examples
>>> from diofant.functions import euler
>>> [euler(n) for n in range(10)] [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0] >>> euler(n+2*n) euler(3*n)
References
factorial
- class diofant.functions.combinatorial.factorials.factorial(n)[source]
Implementation of factorial function over nonnegative integers.
By convention (consistent with the gamma function and the binomial coefficients), factorial of a negative integer is complex infinity.
The factorial is very important in combinatorics where it gives the number of ways in which \(n\) objects can be permuted. It also arises in calculus, probability, number theory, etc.
There is strict relation of factorial with gamma function. In fact n! = gamma(n+1) for nonnegative integers. Rewrite of this kind is very useful in case of combinatorial simplification.
Computation of the factorial is done using two algorithms. For small arguments naive product is evaluated. However for bigger input algorithm Prime-Swing is used. It is the fastest algorithm known and computes n! via prime factorization of special class of numbers, called here the ‘Swing Numbers’.
Examples
>>> factorial(0) 1
>>> factorial(7) 5040
>>> factorial(-2) zoo
>>> factorial(n) factorial(n)
>>> factorial(2*n) factorial(2*n)
>>> factorial(Rational(1, 2)) factorial(1/2)
subfactorial
- class diofant.functions.combinatorial.factorials.subfactorial(arg)[source]
The subfactorial counts the derangements of n items and is defined for non-negative integers as:
, | 1 for n = 0 !n = { 0 for n = 1 | (n - 1)*(!(n - 1) + !(n - 2)) for n > 1 `
It can also be written as int(round(n!/exp(1))) but the recursive definition with caching is implemented for this function.
An interesting analytic expression is the following
\[!x = \Gamma(x + 1, -1)/e\]which is valid for non-negative integers x. The above formula is not very useful in case of non-integers. \(\Gamma(x + 1, -1)\) is single-valued only for integral arguments x, elsewhere on the positive real axis it has an infinite number of branches none of which are real.
References
Examples
>>> subfactorial(n + 1) subfactorial(n + 1) >>> subfactorial(5) 44
factorial2 / double factorial
- class diofant.functions.combinatorial.factorials.factorial2(n)[source]
The double factorial n!!, not to be confused with (n!)!
The double factorial is defined for nonnegative integers and for odd negative integers as:
, | n*(n - 2)*(n - 4)* ... * 1 for n positive odd n!! = { n*(n - 2)*(n - 4)* ... * 2 for n positive even | 1 for n = 0 | (n+2)!! / (n+2) for n negative odd `
References
Examples
>>> factorial2(n + 1) factorial2(n + 1) >>> factorial2(5) 15 >>> factorial2(-1) 1 >>> factorial2(-5) 1/3
FallingFactorial
- class diofant.functions.combinatorial.factorials.FallingFactorial(x, k)[source]
Falling factorial (related to rising factorial) is a double valued function arising in concrete mathematics, hypergeometric functions and series expansions.
It is defined by:
ff(x, k) = x * (x-1) * ... * (x - k+1)
where ‘x’ can be arbitrary expression and ‘k’ is an integer. For more information check “Concrete mathematics” by Graham, pp. 66 or visit https://mathworld.wolfram.com/FallingFactorial.html page.
>>> ff(x, 0) 1
>>> ff(5, 5) 120
>>> ff(x, 5) == x*(x-1)*(x-2)*(x-3)*(x-4) True
fibonacci
- class diofant.functions.combinatorial.numbers.fibonacci(n, sym=None)[source]
Fibonacci numbers / Fibonacci polynomials
The Fibonacci numbers are the integer sequence defined by the initial terms F_0 = 0, F_1 = 1 and the two-term recurrence relation F_n = F_{n-1} + F_{n-2}. This definition extended to arbitrary real and complex arguments using the formula
\[F_z = \frac{\phi^z - \cos(\pi z) \phi^{-z}}{\sqrt 5}\]The Fibonacci polynomials are defined by F_1(x) = 1, F_2(x) = x, and F_n(x) = x*F_{n-1}(x) + F_{n-2}(x) for n > 2. For all positive integers n, F_n(1) = F_n.
fibonacci(n) gives the nth Fibonacci number, F_n
fibonacci(n, x) gives the nth Fibonacci polynomial in x, F_n(x)
Examples
>>> [fibonacci(x) for x in range(11)] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] >>> fibonacci(5, Symbol('t')) t**4 + 3*t**2 + 1
References
harmonic
- class diofant.functions.combinatorial.numbers.harmonic(n, m=None)[source]
Harmonic numbers
The nth harmonic number is given by \(\operatorname{H}_{n} = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n}\).
More generally:
\[\operatorname{H}_{n,m} = \sum_{k=1}^{n} \frac{1}{k^m}\]As \(n \rightarrow \infty\), \(\operatorname{H}_{n,m} \rightarrow \zeta(m)\), the Riemann zeta function.
harmonic(n)
gives the nth harmonic number, \(\operatorname{H}_n\)harmonic(n, m)
gives the nth generalized harmonic number of order \(m\), \(\operatorname{H}_{n,m}\), whereharmonic(n) == harmonic(n, 1)
Examples
>>> [harmonic(n) for n in range(6)] [0, 1, 3/2, 11/6, 25/12, 137/60] >>> [harmonic(n, 2) for n in range(6)] [0, 1, 5/4, 49/36, 205/144, 5269/3600] >>> harmonic(oo, 2) pi**2/6
>>> harmonic(n).rewrite(Sum) Sum(1/_k, (_k, 1, n))
We can evaluate harmonic numbers for all integral and positive rational arguments:
>>> harmonic(8) 761/280 >>> harmonic(11) 83711/27720
>>> H = harmonic(Rational(1, 3)) >>> H harmonic(1/3) >>> He = expand_func(H) >>> He -log(6) - sqrt(3)*pi/6 + 2*Sum(log(sin(pi*_k/3))*cos(2*pi*_k/3), (_k, 1, 1)) + 3*Sum(1/(3*_k + 1), (_k, 0, 0)) >>> He.doit() -log(6) - sqrt(3)*pi/6 - log(sqrt(3)/2) + 3 >>> H = harmonic(Rational(25, 7)) >>> He = simplify(expand_func(H).doit()) >>> He log(sin(pi/7)**(-2*cos(pi/7))*sin(2*pi/7)**(2*cos(16*pi/7))*cos(pi/14)**(-2*sin(pi/14))/14) + pi*tan(pi/14)/2 + 30247/9900 >>> He.evalf(40) 1.983697455232980674869851942390639915940 >>> harmonic(Rational(25, 7)).evalf(40) 1.983697455232980674869851942390639915940
We can rewrite harmonic numbers in terms of polygamma functions:
>>> harmonic(n).rewrite(digamma) polygamma(0, n + 1) + EulerGamma
>>> harmonic(n).rewrite(polygamma) polygamma(0, n + 1) + EulerGamma
>>> harmonic(n, 3).rewrite(polygamma) polygamma(2, n + 1)/2 - polygamma(2, 1)/2
>>> harmonic(n, m).rewrite(polygamma) (-1)**m*(polygamma(m - 1, 1) - polygamma(m - 1, n + 1))/factorial(m - 1)
Integer offsets in the argument can be pulled out:
>>> expand_func(harmonic(n+4)) harmonic(n) + 1/(n + 4) + 1/(n + 3) + 1/(n + 2) + 1/(n + 1)
>>> expand_func(harmonic(n-4)) harmonic(n) - 1/(n - 1) - 1/(n - 2) - 1/(n - 3) - 1/n
Some limits can be computed as well:
>>> limit(harmonic(n), n, oo) oo
>>> limit(harmonic(n, 2), n, oo) pi**2/6
>>> limit(harmonic(n, 3), n, oo) -polygamma(2, 1)/2
However we can not compute the general relation yet:
>>> limit(harmonic(n, m), n, oo) harmonic(oo, m)
which equals
zeta(m)
form > 1
.References
lucas
- class diofant.functions.combinatorial.numbers.lucas(n)[source]
Lucas numbers
Lucas numbers satisfy a recurrence relation similar to that of the Fibonacci sequence, in which each term is the sum of the preceding two. They are generated by choosing the initial values L_0 = 2 and L_1 = 1.
lucas(n) gives the nth Lucas number
Examples
>>> [lucas(x) for x in range(11)] [2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123]
References
See also
diofant.functions.combinatorial.numbers.bell
,diofant.functions.combinatorial.numbers.bernoulli
,diofant.functions.combinatorial.numbers.catalan
,diofant.functions.combinatorial.numbers.euler
,diofant.functions.combinatorial.numbers.fibonacci
,diofant.functions.combinatorial.numbers.harmonic
RisingFactorial
- class diofant.functions.combinatorial.factorials.RisingFactorial(x, k)[source]
Rising factorial (also called Pochhammer symbol) is a double valued function arising in concrete mathematics, hypergeometric functions and series expansions.
It is defined by:
rf(x, k) = x * (x+1) * ... * (x + k-1)
where ‘x’ can be arbitrary expression and ‘k’ is an integer. For more information check “Concrete mathematics” by Graham, pp. 66 or visit https://mathworld.wolfram.com/RisingFactorial.html page.
Examples
>>> rf(x, 0) 1
>>> rf(1, 5) 120
>>> rf(x, 5) == x*(1 + x)*(2 + x)*(3 + x)*(4 + x) True
stirling
- diofant.functions.combinatorial.numbers.stirling(n, k, d=None, kind=2, signed=False)[source]
Return Stirling number S(n, k) of the first or second (default) kind.
The sum of all Stirling numbers of the second kind for k = 1 through n is bell(n). The recurrence relationship for these numbers is:
{0} {n} {0} {n + 1} {n} { n } { } = 1; { } = { } = 0; { } = j*{ } + { } {0} {0} {k} { k } {k} {k - 1}
- where
j
is:: n
for Stirling numbers of the first kind-n
for signed Stirling numbers of the first kindk
for Stirling numbers of the second kind
The first kind of Stirling number counts the number of permutations of
n
distinct items that havek
cycles; the second kind counts the ways in whichn
distinct items can be partitioned intok
parts. Ifd
is given, the “reduced Stirling number of the second kind” is returned:S^{d}(n, k) = S(n - d + 1, k - d + 1)
withn >= k >= d
. (This counts the ways to partitionn
consecutive integers intok
groups with no pairwise difference less thand
. See example below.)To obtain the signed Stirling numbers of the first kind, use keyword
signed=True
. Using this keyword automatically setskind
to 1.Examples
>>> import itertools >>> from diofant.utilities.iterables import multiset_partitions
First kind (unsigned by default):
>>> [stirling(6, i, kind=1) for i in range(7)] [0, 120, 274, 225, 85, 15, 1] >>> perms = list(itertools.permutations(range(4))) >>> [sum(Permutation(p).cycles == i for p in perms) for i in range(5)] [0, 6, 11, 6, 1] >>> [stirling(4, i, kind=1) for i in range(5)] [0, 6, 11, 6, 1]
First kind (signed):
>>> [stirling(4, i, signed=True) for i in range(5)] [0, -6, 11, -6, 1]
Second kind:
>>> [stirling(10, i) for i in range(12)] [0, 1, 511, 9330, 34105, 42525, 22827, 5880, 750, 45, 1, 0] >>> sum(_) == bell(10) True >>> len(list(multiset_partitions(range(4), 2))) == stirling(4, 2) True
Reduced second kind:
>>> def delta(p): ... if len(p) == 1: ... return oo ... return min(abs(i[0] - i[1]) for i in itertools.combinations(p, 2)) >>> parts = multiset_partitions(range(5), 3) >>> d = 2 >>> sum(1 for p in parts if all(delta(i) >= d for i in p)) 7 >>> stirling(5, 3, 2) 7
References
- where