Stars And Bars Isn't Just A Textbook Trick
Most people learn stars and bars as a way to count solutions to x1 + x2 + ... + xk = n where all xi are non-negative integers. That's the basic version. The constrained version is what actually comes up when you're trying to optimize something real, like allocating budget across projects or distributing work shifts across teams. The math gets messier fast once you add upper bounds, lower bounds, or conditional constraints. I've spent enough time wrestling with these problems to know that the standard formula breaks down the moment you throw constraints at it. Here's the thing nobody tells you: stars and bars with constraints isn't one method. It's a whole family of approaches, and picking the wrong one will waste your afternoon. Inclusion-exclusion is the first tool most people reach for, and for a small number of constraints it works fine. But once you have five or more upper-bound constraints on your variables, the inclusion-exclusion sum has 2^5 = 32 terms, and each term is another stars and bars calculation. The arithmetic doesn't explode, but the error surface does. One sign mistake in the inclusion-exclusion and you're off by an order of magnitude with no warning.
Unlock The Power Of Combinatorial Optimization With Stars And Bars With Constraints
The practical workflow starts with identifying what kind of constraints you're dealing with. There are three main types: lower bounds, upper bounds, and equality or inequality constraints between variables. Lower bounds are the easy part. If x1 >= 2, you just substitute y1 = x1 - 2 and solve the reduced problem. This is covered in every textbook because it's genuinely straightforward. Upper bounds are where things get interesting, and they're also where most people mess up. For upper bounds, I almost always use generating functions now instead of inclusion-exclusion. The coefficient of x^n in the expansion of (1 + x + x^2 + ... + x^a1)(1 + x + x^2 + ... + x^a2)...(1 + x + x^2 + ... + x^ak) gives you exactly the number of constrained solutions. Each factor represents one variable and its allowed range. This seems less direct than inclusion-exclusion at first glance, but it scales much better and it's less prone to sign errors. When I worked on a resource allocation problem last year with six variables each capped at different upper bounds summing to around forty, inclusion-exclusion would have required 64 terms. The generating function approach through polynomial multiplication was faster and more reliable. Let me walk through a concrete example that mirrors what I actually dealt with recently. Say you need to distribute 20 identical units among 5 projects where each project must receive at least 2 units but no more than 7. First step is handling the lower bounds. Subtract 2 from each variable, which reduces the problem to distributing 10 units among 5 variables with each between 0 and 5. Now you need the coefficient of x^10 in (1 + x + x^2 + x^3 + x^4 + x^5)^5. Expanding this by hand is tedious but manageable. Using the geometric series formula, each factor equals (1 - x^6)/(1 - x), so the product becomes (1 - x^6)^5 * (1 - x)^(-5). The second part expands via the negative binomial theorem into sum of C(n+4,4)*x^n. The first part expands via the binomial theorem into sum of C(5,k)*(-1)^k*x^(6k). You convolve these two series and pick out the x^10 term. That gives C(14,4) - 5*C(8,4) = 1001 - 5*70 = 651. So there are 651 valid distributions.
What trips people up is the convolution step. They compute the individual expansions correctly but then miss that they need to pair terms whose exponents sum to 10. The term from (1-x^6)^5 with x^0 pairs with the x^10 term from the negative binomial expansion. The x^6 term from the first part pairs with the x^4 term from the second. There's no x^12 term in the first part that could contribute since 12 > 10. Writing this as a single sum: the coefficient of x^10 equals sum over k of (-1)^k * C(5,k) * C(10-6k+4, 4) where the binomial coefficient is zero whenever the top is negative or smaller than the bottom. This is the general formula for upper-bounded stars and bars and it's worth memorizing because it shows up repeatedly. Now here's a counter-intuitive point that took me a while to internalize. When you have many variables with identical upper bounds, the generating function approach can sometimes be slower than inclusion-exclusion. If you have 20 variables each bounded by 3 and you're distributing 30 units, the generating function is (1 + x + x^2 + x^3)^20, and extracting a single coefficient requires careful bookkeeping. Inclusion-exclusion in this case needs only the terms where k ranges from 0 to 10 (since 4k
= 20 for the exponent to stay under 30), and each term is a simple binomial coefficient. The two methods have different complexity profiles depending on the parameters, and neither dominates universally. Another pitfall I want to flag: equality constraints between variables. If you have x1 = x2, you don't need a separate constraint technique. Just substitute x2 for x1 everywhere and reduce the variable count by one. But if you have x1
= x2, that's different and significantly harder. That inequality constraint couples two variables in a way that breaks the standard generating function symmetry. I dealt with a scheduling problem last year where certain shifts had to be non-decreasing across workers, and the inclusion-exclusion framework didn't apply cleanly because the constraint region isn't a simple box. I ended up partitioning the domain based on the relative ordering of variables and solving each subproblem separately, then summing. It added roughly three times the computational overhead but it was the only approach that didn't involve guessing and checking.
Get the Full Details

For people who need to compute these counts repeatedly or at scale, there's a practical note about implementation. Dynamic programming is often the most robust approach for constrained stars and bars. You build a table where dp[i][j] represents the number of ways to distribute j units among the first i variables respecting their constraints. The recurrence is dp[i][j] = sum over m of dp[i-1][j-m] where m ranges over valid values for the i-th variable. This runs in O(k * n * max_bound) time, which for most practical problems is faster than computing large inclusion-exclusion sums and much easier to debug. I wrote a quick Python implementation for a logistics problem once and it handled 12 variables with varying bounds in under a second. The code was about twenty lines including comments. One limitation worth stating plainly: stars and bars with constraints only counts solutions. It doesn't optimize anything. If you're actually trying to minimize or maximize a linear function subject to these constraints, you're in integer programming territory and the counting formulas become irrelevant. I've seen people try to use stars and bars counts as a heuristic for optimization problems, which doesn't work because the most numerous configurations aren't necessarily the optimal ones. When optimization is the goal, branch-and-bound or cutting plane methods are the right tools. Stars and bars serves as a useful preprocessing step in some integer programming contexts—you can prune branches by computing whether any feasible solution exists in a subspace—but it shouldn't be confused with an optimization technique itself. There's also a boundary condition that catches people off guard. If the sum of the lower bounds exceeds n, or if n exceeds the sum of the upper bounds, the answer is zero. This sounds obvious but in automated systems I've built, skipping this check led to hours of debugging where the inclusion-exclusion formula produced nonsensical negative values because I hadn't validated the feasibility region before running the calculation. Always check feasibility first. It takes two lines of code and prevents cascading errors.
For the edge case I mentioned earlier—the one with conditional constraints where x1 > 0 only if x2
5—I solved it by splitting into two disjoint cases. Case one: x2 ranges from 0 to 4, and x1 is free above its lower bound. Case two: x2 ranges from 5 upward, and x1 must be zero. Each case is a standard constrained stars and bars problem. You solve them independently and add the results. The total computation was maybe 20% slower than a single unconstrained run, which is reasonable given that the constraint structure isn't captured by the basic formula. If you're starting out with this topic, I'd recommend working through the generating function method first before diving into inclusion-exclusion. The polynomial intuition is more durable and transfers better to related problems like partition counting and coefficient extraction. The inclusion-exclusion approach is powerful but fragile—every additional constraint doubles your term count and your chance of a sign error. Once you're comfortable with the generating function framework, inclusion-exclusion becomes a useful backup rather than your default tool.
