I am an amateur mathematician from the OEIS, and use math.comb and math.perm frequently in my contributions there, during which it often behooves me to define
for reasons that I will try to convince you of here!
it continues each column and row backwards as a polynomial! (The one compromise is that the point (0,0) does not obey C(n,k) = C(n-1,k) + C(n-1,k-1), but this has to be broken to retain C(n,k) = C(n,n-k).)
The gamma function has poles at the negative integers; if you change the factorials inside C(n,k) = n!/(k!*(n-k)!) with x!âÎ(x+1), then take the limit as you approach integer values of n,k (with n negative), Î(n+1) is at a pole, and one of Î(k+1) and Î(n-k+1) (depending on kâs sign). Since the poles are simple (the residue of Î(-n) is (-1)âż/n!), one can use the opposite of LâHĂŽpitalâs rule (replace poles with residues, rather than zeros with derivatives) to obtain the limit as n,k approach integers (in any pair of directions in their complex planes).
You can rewrite the factorials as products, extend products to negative-cardinality ranges, and cancel the zeroes, for the same result. (See Extensions of the binomial by Peter Luschny (which gives an argument with a bidirectional matrix), this section of my page which is primarily about extending Stirling numbers.)
(n choose k) counts the ways to create a k-set by drawing from an n-set. Via a stars and bars argument, one obtains that the function ((n multichoose k)) = (n+k-1 choose k) counts the ways to create a k-multiset by drawing with replacement.
Increasing the minimum number of stars between each bar-pair is equivalent to making the interval shorter, so (defining for our convenience) (n part k) := (n-1 choose k-1) is the number of ways to partition the integer n into an ordered list of k positive integer parts. For these purposes, it is useful to have the rule (n choose -1) = int(n==-1) to yield (n part 0) = int(n==0), since we consider there to be one partition of 0 into 0 positive parts. (In general, when each part is >=c, the # of partitions is (n-1+(1-c)*k choose k-1), with ((k multichoose n)) being the c=0 case.) This point, I think, is the one most meaningful for people using Python.
I would like to be able to write comb(n,k,cont=True) to enable the continuation outside the doubly-nonnegative quadrant.
For perm(n,k) = k! * comb(n,k) I would like the first factor to be permitted to be negative, since allowing the second to be would entail floats which arenât so useful for discrete math; perhaps a fractions.perm function would be preferable?
I supported defining comb(n,k) for k < 0, like it is defined for k > n, but was outnumbered.
Extending it to n < 0 is more ambiguous. You will need to break one of nice properties. In your example it is C(n,k) = C(n-1,k) + C(n-1,k-1), but there are other possible variants.
CPython doesnât intend to be âa pioneerâ in numeric areas. Thatâs for specialized extensions, and in the core we fry to stick to things everyone agrees on. For comb(n, k), a case may be made for negative n, but not for negative k, because prior Python related art already does different things for negative k:
>>> from gmpy2 import comb
>>> comb(4, 2)
mpz(6)
>>> comb(-4, 2)
mpz(10)
>>> comb(-4, 3)
mpz(-20)
>>> comb(4, -2)
Traceback (most recent call last):
...
comb(4, -2)
~~~~^^^^^^^
OverflowError: can't convert negative value to unsigned int
Well, this is just a bug. This incompatibility wrt the CPython is not essential, better to get rid of it.
Though, Mathematica does, for instance:
Wolfram Language 14.3.0 Engine for Linux x86 (64-bit)
Copyright 1988-2025 Wolfram Research, Inc.
In[1]:= Binomial[4, -2]
Out[1]= 0
In[2]:= Binomial[4, 4- -2]
Out[2]= 0
I donât think we should be biased by particular choice of the GNU GMP.
But there are several ways of extension for comb(): note the PascalBinomial() function along side with the Binomial().
I encourage OP to present a new documentation for the comb(). Right now this (and perm() too) has simple combinatorial meaning, but it not holds for negative arguments. We need to explain definition here, maybe also reasons for a particular choice, etc. Thinking about all this stuff â maybe itâs better suited for an external package.
Neither do I. Can only repeat that core Python shouldnât take stands on fringe behaviors when other specialized packages already do different things. with them. Iâm biased neither for nor against what GMP does here, just that itâs evidence there is not universal agreement.
There is universal agreement about what comb() should do when both arguments are non-negative, and thatâs what the core implements, and thatâs what the core should stick to.
Yes, and âpick oneâ is not our area of competence.
Of which there are already several to pick from.
Which is more likely: that a typical Python programmer passing -2 as the second argument to comb() intended that, or that it was a logical error in their code? âBad argumentsâ have value too, and especially for catching errors in code written by non-experts. Itâs why sqrt(-1) raises an exception in Python, despite that the language supports complex numbers.
Those who want 1j can get it cmath.sqrt(), used by those who know what theyâre doing.
But I expect the audience for negative arguments to comb() is far smaller than the one for complex numbers. I wonât give any time to it.
BTW, playing around with Wolfram Alpha on the web, its Binomial appears to accept arbitrary real and complex numbers too. presumably (but donât know this) by rewriting as gamma function calls when an arg isnât an int.
I prefer that math.comb and math.comb remain harmonized with itertools.combinations and itertools.permutations neither of which have any concept of negative n or negative k.
As a mathematician myself, I would prefer that we do not change math.comb because itâs not the exactly the same as binom and neither is it the same as just using \Gamma. math.comb is really meant to represent the â[n]umber of ways to choose k items from n items without repetition and without orderâ. The binomial coefficient \binom{x}{y} coincides with the output of math.comb when x and y are nonnegative integers but conceptually, it can also be regarded as function of two variables defined through the \Gamma function. SciPy provides analytic continuation for that function (and makes the distinction with comb) but returns nan for negative x since we have a pole.
If we raise error for k < 0, then we should also raise error for k > n, because this function is symmetric.
For math.comb, I agree that we should actually allow comb(n, k) == 0 for k > n because there is no way to pick k items out n without repetition or order. But I donât want to motivate this choice by the identity \binom{n}{k} = \binom{n}{n-k} as it is (strictly speaking) only well-defined for 0\leq k\leq n.
My take is: donât change the meaning of math.comb. Its meaning is a combinatoric one, period. There is just no canonical definition of \binom{x}{y} for negative arguments unless we make a choice, but this is not a choice the standard library should be willing to make (at least to me). The article you linked with the Pascal, Riordan and Taylor conditions looks good in the sense that Maple and Mathematica made that choice (I guess this is based on generalization to negative integers n and compled-valued binomial coefficient).
another point that I forgot to mention in the initial proposal is that with this convention, for negative n we would have that sum(comb(n,k,cont=1)*x**k for k in range(M)) and sum(comb(n,n-k,cont=1)*x**k for k in range(M)) converge to (1+x)**n for |x|<1 and |x|>1 respectively. (This is tautological on its own, but becomes useful when doing things like getting an Mth-order approximation of the Laplace transform of a product of functions).
I prefer that math.comb and math.comb remain harmonized with itertools.combinations and itertools.permutations neither of which have any concept of negative n or negative k.
well, the negative-nmath.comb(âs absolute value) would be harmonized with itertools.combinations_with_replacement.
I used to do combinatorics questions at mathematics competitions. And in that context, I sometimes found the interpretation where binomial(n, k) is a polynomial in n of degree k useful. So just because [combâs] meaning is the combinatoric one, that doesnât even necessarily mean that n needs to be an integer
But I can respect the standard-lib choosing to Error rather aggressively for functions like this.
But you spelled it binomial, not comb. And that seems common when going beyond the combinatorial definition (where the inputs and outputs are cardinalities of finite sets, and so necessarily non-negative integers).
For example, under Maxima:
(%i0) binomial(n, 3);
(%o0) ((n-2)*(n-1)*n)/6
My replies would be different if the the suggestion were to add a newbinomial function. As is, this is akin to asking that math.factorial accept floats too as a way to spell a shifted variant of math.gamma.
Such things donât serve the vast bulk of users, to whom phrases like âanalytic continuationâ sound like AI hallucinations
The result of the combinatorial meaning of comb() is captured by a formula. The formula can be generalized in various ways that âmake senseâ in contexts beyond combinatorics, but doing so obscures the original - and intended, and simple - meaning.
Better to leave those to packages aimed at domain experts.