Why Stars and Bars Matters
Suppose a service assigns five identical units of capacity to three named queues. A queue may receive nothing, and only its allocation count matters. Allocations such as $(2,0,3)$ and $(0,2,3)$ differ because the queues are labeled. How many allocation vectors are possible? Enumerating them works for a toy example, but the number grows quickly.
Stars and bars turns this allocation problem into choosing separator positions. It counts nonnegative integer solutions to a fixed-sum equation, a pattern that appears in resource allocation, inventory counts, monomials, and test-data generation. After this lesson, you will be able to recognize the model, derive its binomial coefficient, and compute the answer with the same dynamic program in Python, C++, and Java. The recurrence builds on recursion and dynamic-programming foundations: define a state, establish its boundaries, then reuse overlapping subproblems.
Stars and bars counts distributions of $n$ indistinguishable objects among $k$ labeled bins, allowing empty bins. For $k\ge1$, inserting $k-1$ identical separators among $n$ stars gives a bijection with allocations, so the count is $\binom{n+k-1}{k-1}$.
Core Concepts and Terminology
The inputs are integers $n\ge0$ and $k\ge0$. The exact answer counts ordered tuples $(x_1,\ldots,x_k)$ satisfying
$$ x_1+\cdots+x_k=n,\qquad x_i\ge0. $$
For $k\ge1$, write $n$ stars and $k-1$ bars in one string. The stars before the first bar belong to bin one; those between consecutive bars belong to the next bin; those after the last bar belong to the final bin. Leading, trailing, and adjacent bars are legal: they represent empty bins. Both the objects and the separators are indistinguishable within their own type.
There are $n+k-1$ positions in the string. Choosing the $k-1$ bar positions determines all remaining star positions, giving
$$ W(n,k)=\binom{n+k-1}{k-1}=\binom{n+k-1}{n}\qquad(k\ge1). $$
Handle zero bins separately. There is one empty allocation of zero objects, so $W(0,0)=1$; distributing a positive number of objects among zero bins is impossible, so $W(n,0)=0$ for $n\gt0$. With at least one bin, $W(0,k)=1$. Negative inputs are invalid. The programs return $W(n,k)\bmod M$ for the explicit modulus $M=1{,}000{,}000{,}007$, rather than the unbounded exact count.
| Term | Meaning | Example and role |
|---|---|---|
| Star | One indistinguishable object | $n=5$ produces five stars |
| Bar | Separator between consecutive labeled bins | $k=3$ requires $k-1=2$ bars |
| Weak composition | An ordered tuple of nonnegative integers with fixed sum | $(2,0,3)$ is a weak composition of $5$ into $3$ parts |
| Positive composition | Every part must be positive | $(2,1,2)$ is valid; $(2,0,3)$ is excluded |
| Binomial coefficient | Number of subsets of a specified size | $\binom{7}{2}=21$ chooses the two bar positions |
| DP state $F(b,s)$ | Allocations of $s$ objects among $b$ labeled bins | $F(3,5)=21$; thus $W(n,k)=F(k,n)$ |
Rolling array ways[s] | Residue for the current or preceding DP row | Ascending updates retain precisely the two needed neighbors |
| Modulus $M$ | Bounds every stored count | Modular addition preserves the exact answer's residue |
How Stars and Bars Works, Step by Step
The combinatorial formula identifies the answer; the implementation below evaluates the same count without factorials or division. Define $F(b,s)$ as above. For $b\ge1$ and $s\ge1$, split allocations according to the last bin. If it is empty, delete that bin, leaving $F(b-1,s)$ choices. If it is nonempty, remove one object from it, leaving $F(b,s-1)$ choices. These cases are disjoint and reversible, so
$$ F(b,s)=F(b-1,s)+F(b,s-1). $$
The boundaries are $F(0,0)=1$, $F(0,s)=0$ for $s\gt0$, and $F(b,0)=1$. This is Pascal's addition rule on a rectangular state grid.
- Reject negative inputs. Return $1$ for $n=0$, including the empty allocation when $k=0$; otherwise return $0$ for $k=0$. Return $1$ for $k=1$.
- Allocate
wayswith $n+1$ entries. Setways[0]to $1$ and all other entries to $0$: this is row $b=0$. - Add one labeled bin per outer iteration. Sweep totals $s=1,\ldots,n$ in ascending order.
- Replace
ways[s]by(ways[s] + ways[s - 1]) % MOD. The first term is still $F(b-1,s)$; the second has already become $F(b,s-1)$, both represented modulo $M$. - Keep
ways[0]equal to $1$. After all $k$ iterations, returnways[n].
The direction of the sweep is part of the algorithm. During the second bin's sweep for $n=5$, the update at $s=3$ must add the old value $1$ to the already-updated value $3$, producing $4$. The figure shows that exact transition.
flowchart TD
A["Read n and k"] --> B{"Negative input?"}
B -- Yes --> C["Reject input"]
B -- No --> D{"n equals zero?"}
D -- Yes --> E["Return 1"]
D -- No --> F{"k equals zero?"}
F -- Yes --> G["Return 0"]
F -- No --> H{"k equals one?"}
H -- Yes --> E
H -- No --> I["Initialize ways and processed = 0"]
I --> J{"processed less than k?"}
J -- No --> K["Return ways at n"]
J -- Yes --> L["Set s = 1"]
L --> N{"s at most n?"}
N -- Yes --> O["Add current left neighbor modulo MOD"]
O --> P["Increment s"]
P --> N
N -- No --> Q["Increment processed"]
Q --> J
Worked Example with a Full Trace
Take $n=5$ and $k=3$. Every state below is smaller than $M$, so reduction does not change its value. Initialization gives [1, 0, 0, 0, 0, 0]. Each list records totals from $s=0$ through $s=5$.
For bin $b=1$, the five updates are $0+1=1$, $0+1=1$, $0+1=1$, $0+1=1$, and $0+1=1$. After each update, the array is respectively [1, 1, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 0], and [1, 1, 1, 1, 1, 1]. With one bin, every total has exactly one allocation.
For bin $b=2$, the updates are $1+1=2$, $1+2=3$, $1+3=4$, $1+4=5$, and $1+5=6$. The arrays become [1, 2, 1, 1, 1, 1], [1, 2, 3, 1, 1, 1], [1, 2, 3, 4, 1, 1], [1, 2, 3, 4, 5, 1], and [1, 2, 3, 4, 5, 6].
For bin $b=3$, the updates are $2+1=3$, $3+3=6$, $4+6=10$, $5+10=15$, and $6+15=21$. The arrays become [1, 3, 3, 4, 5, 6], [1, 3, 6, 4, 5, 6], [1, 3, 6, 10, 5, 6], [1, 3, 6, 10, 15, 6], and [1, 3, 6, 10, 15, 21].
Thus $W(5,3)=21$, agreeing with $\binom{7}{2}=21$. Another direct check fixes the first bin at $0,1,2,3,4,5$ objects: the remaining two bins admit $6,5,4,3,2,1$ allocations, whose sum is $21$.
Trace It by Hand
Pause during the third bin's sweep after updating total $s=2$. The array is [1, 3, 6, 4, 5, 6]. Predict the next array and identify which values come from which DP rows before reading on.
The next update uses old ways[3] equal to $F(2,3)=4$ and new ways[2] equal to $F(3,2)=6$. It writes $10$, producing [1, 3, 6, 10, 5, 6]. Then write $15$ and $21$ to finish. As a separate exercise, use the completed row to predict $W(3,3)$: its entry at total $3$ is $10$.
Implementation in Python, C++, and Java
Each program defines the same count_distributions operation, uses the same boundaries and ascending updates, and prints the same samples. No input is read: the sample pairs are built in, making every panel independently runnable. In Java, save the program as Main.java.
All arithmetic is integral. Python uses arbitrary-precision integers, C++ uses long long, and Java uses BigInteger. Reduction occurs after every addition. In C++, both operands lie between $0$ and $M-1$, so their sum is at most $2M-2=2{,}000{,}000{,}012$, safely within long long; no factorial or intermediate product can overflow. Inputs must be nonnegative and the array must fit memory. Java's checked length calculation also rejects a total whose array length cannot be represented by an int.
All three programs print the following lines: modulus = 1000000007; W(0,0) = 1; W(5,0) = 0; W(0,3) = 1; W(5,1) = 1; W(5,3) = 21; W(3,5) = 35; W(4,2) = 5; W(5,5) = 126; W(30,20) = 684765627. The final value is a residue; the small preceding values equal their exact counts.
Complexity, Correctness, and Invariants
On the general path $n\gt0$ and $k\ge2$, the programs perform exactly $nk$ additions and reductions, plus array initialization. Best, average, and worst running times on that path are all $\Theta(nk)$: the allocation counts do not affect control flow. Auxiliary space is $\Theta(n)$ residues. Inputs covered by the shortcuts $n=0$, $k=0$, or $k=1$ take $\Theta(1)$ time and space. An average over arbitrary inputs needs an input distribution; there is no additional data-dependent average-case speedup.
These bounds treat the fixed modulus as constant-sized arithmetic. With a variable modulus, arithmetic also depends on its bit length. Removing every reduction computes the exact answer in Python or Java, but the growing integer sizes add bit complexity; the C++ implementation would need arbitrary-precision integers.
The formula itself has a short proof: read each allocation as a unique string and recover the allocation by counting stars in its consecutive regions. This reversible correspondence proves that choosing bar positions neither loses nor duplicates a tuple. The DP proof below shows that the programs compute the same count, including its boundaries.
| Method | Time | Auxiliary space | Assumptions and tradeoff |
|---|---|---|---|
| Rolling allocation DP shown here | $\Theta(nk)$ on the general path | $\Theta(n)$ | Uses additions only; works for every positive modulus |
| Full state table $F(b,s)$ | $\Theta(nk)$ | $\Theta(nk)$ | Retains all rows for inspection or later queries |
| Naive enumeration of all tuples | At least $\Omega(kW(n,k))$ to output every tuple | $O(k)$ recursion state, excluding output | Useful for listing tiny instances, not just counting |
| Exact multiplicative binomial | $O(q)$ big-integer multiply/divide steps | A constant number of big integers | $q=\min(n,k-1)$; divide exactly at each step; account for bit costs |
| Factorials and inverse factorials modulo prime $p$ | $O(L+\log p)$ preparation; $O(1)$ per query | $O(L)$ | Precompute through $L\ge n+k-1$ with $L\lt p$; larger tops need another method |
Before updating total $s$ in bin row $b$, entries with index below $s$ hold $F(b,t)\bmod M$, while entries from $s$ onward still hold $F(b-1,t)\bmod M$. Adding the old entry and its current-row left neighbor establishes $F(b,s)\bmod M$; initialization, the unchanged zero-total entry, and induction over both loops therefore prove the returned value correct.
Variants, Pitfalls, and Optimizations
Positive parts. If every bin must receive at least one object, set $y_i=x_i-1$. For $k\ge1$ and $n\ge k$, the answer becomes $W(n-k,k)=\binom{n-1}{k-1}$; for $n\lt k$ it is zero. For example, five objects in three nonempty bins admit $\binom{4}{2}=6$ allocations, compared with $21$ when empty bins are allowed. Zero bins still require the separate empty-allocation convention.
Arbitrary lower bounds. For $x_i\ge\ell_i$ with nonnegative integer lower bounds, subtract each bound and count $W(n-\sum_i\ell_i,k)$ if the remaining total is nonnegative. If it is negative, return zero rather than passing an invalid total into the function.
Upper bounds. A capacity restriction invalidates the unrestricted formula. For a uniform cap $0\le x_i\le u$ and $k\ge1$, inclusion-exclusion gives
$$ \sum_{j=0}^{\min(k,\lfloor n/(u+1)\rfloor)} (-1)^j\binom{k}{j} \binom{n-j(u+1)+k-1}{k-1}. $$
Each chosen violating bin is shifted down by $u+1$. For $n=5$, $k=3$, and $u=2$, the count is $21-3\binom{4}{2}=3$: the permutations of $(2,2,1)$. For unequal caps, use subset inclusion-exclusion or a DP that respects each capacity; a sliding window can accelerate the latter.
Totals at most a limit. To count tuples with $\sum_i x_i\le n$, add a slack variable representing unused capacity. The result is $W(n,k+1)=\binom{n+k}{k}$, including the single empty tuple when $k=0$.
Choose the model carefully. Distinguishable objects assigned independently to labeled bins give $k^n$ functions. Unlabeled bins require an integer-partition model, with any bin-count restriction retained. Neither is the weak-composition problem solved here.
Compute a binomial when that is cheaper. For a single large instance, the displayed DP may do too much work. Compute $\binom{N}{q}$ with $N=n+k-1$ and $q=\min(n,k-1)$ instead. An exact integer recurrence starts at $c_0=1$ and uses $c_i=c_{i-1}(N-i+1)/i$ for $1\le i\le q$; every division is exact before any final modular reduction. Under a modulus, ordinary division is invalid. Prime-modulus factorial tables require invertible factorials, so the simple method fails once the top reaches the prime; consider Lucas's theorem for that regime. The addition-only DP needs no modular inverses and also works with a composite modulus.
The other frequent implementation error is a descending sweep. Starting from the zero-bin row, one descending pass produces [1, 1, 0, 0, 0, 0] instead of [1, 1, 1, 1, 1, 1]. It has used the preceding row's left neighbor, which prevents the new bin from holding more than one object.
Check that objects are indistinguishable, bins are labeled, and empty bins are permitted before applying $\binom{n+k-1}{k-1}$. Preserve ascending array updates, handle $k=0$ separately, and never perform ordinary division on modular residues when evaluating a binomial coefficient.
Practice Problems and Real-World Use
Begin with the model, then choose the computation. Identify what a star represents, whether swapping two bins changes an outcome, and whether any lower or upper bounds apply. The following are self-contained exercises and concrete uses of the same counting pattern; no external problem statement is required.
| Problem or application | Target result or modeling step | Difficulty |
|---|---|---|
| Allocate identical capacity units to named queues | Count allocations of $5$ units to $3$ queues: $W(5,3)=21$ | Easy |
| Require every queue to receive capacity | Count positive allocations for $n=5$, $k=3$: $W(2,3)=6$ | Easy |
| Count histograms of fixed-size samples | A histogram of $4$ observations over $3$ named categories has $W(4,3)=15$ possible count vectors | Easy |
| Count monomials of a fixed total degree | Degree $4$ in $3$ variables means nonnegative exponents summing to $4$: $15$ monomials | Medium |
| Generate resource-allocation test cases with minimum quotas | For total $8$ and minima $(1,2,0)$, subtract $3$ and count $W(5,3)=21$ | Medium |
| Respect finite inventory capacities | Total $5$ across $3$ item types with cap $2$ each gives $3$ vectors; apply inclusion-exclusion or bounded DP | Hard |
| Count nonnegative tuples with unused budget allowed | For $3$ variables with sum at most $5$, add slack and obtain $W(5,4)=56$ | Medium |
As a final check, enumerate all triples for the worked example and compare their count with the DP. Then impose the cap $2$ and verify that only $(1,2,2)$, $(2,1,2)$, and $(2,2,1)$ remain. These two small cases test both the basic bijection and the point where extra constraints require a different count.