Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

Complete solutions in C++, Python & Java — with step-by-step mathematical explanations

All Problems

Problem 53: Combinatoric Selections

View on Project Euler

Project Euler Problem 53 Solution

EulerSolve provides an optimized solution for Project Euler Problem 53, Combinatoric Selections, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary For each integer \(n\) with \(1 \le n \le 100\), and each \(r\) with \(0 \le r \le n\), consider the binomial coefficient \(\binom{n}{r}\). The task is to count how many pairs \((n,r)\) satisfy \(\binom{n}{r} \gt 10^6\). The key point is that we do not need to evaluate every coefficient with factorials. The implementations sweep through each row from left to right, stop as soon as the row crosses one million, and count the rest of that row from symmetry. Mathematical Approach Let \(T=10^6\). The useful structure comes from comparing consecutive binomial coefficients in the same row. Generating a row without factorials The definition $\binom{n}{r}=\frac{n!}{r!(n-r)!}$ is mathematically correct, but it is not the most convenient way to compute many values in one row. If we divide consecutive coefficients, we get $\frac{\binom{n}{r}}{\binom{n}{r-1}}=\frac{n-r+1}{r},$ so $\binom{n}{r}=\binom{n}{r-1}\frac{n-r+1}{r}.$ Starting from \(\binom{n}{0}=1\), this recurrence generates the row one entry at a time. This is exactly the invariant used in the code: after the update for a given \(r\), the running value is the exact integer \(\binom{n}{r}\). No Pascal triangle table and no factorial array are required....

Detailed mathematical approach

Problem Summary

For each integer \(n\) with \(1 \le n \le 100\), and each \(r\) with \(0 \le r \le n\), consider the binomial coefficient \(\binom{n}{r}\). The task is to count how many pairs \((n,r)\) satisfy \(\binom{n}{r} \gt 10^6\).

The key point is that we do not need to evaluate every coefficient with factorials. The implementations sweep through each row from left to right, stop as soon as the row crosses one million, and count the rest of that row from symmetry.

Mathematical Approach

Let \(T=10^6\). The useful structure comes from comparing consecutive binomial coefficients in the same row.

Generating a row without factorials

The definition $\binom{n}{r}=\frac{n!}{r!(n-r)!}$ is mathematically correct, but it is not the most convenient way to compute many values in one row. If we divide consecutive coefficients, we get $\frac{\binom{n}{r}}{\binom{n}{r-1}}=\frac{n-r+1}{r},$ so $\binom{n}{r}=\binom{n}{r-1}\frac{n-r+1}{r}.$ Starting from \(\binom{n}{0}=1\), this recurrence generates the row one entry at a time.

This is exactly the invariant used in the code: after the update for a given \(r\), the running value is the exact integer \(\binom{n}{r}\). No Pascal triangle table and no factorial array are required.

Why the left half is enough

Two standard facts control the whole row: $\binom{n}{r}=\binom{n}{n-r}$ and $\frac{\binom{n}{r}}{\binom{n}{r-1}}=\frac{n-r+1}{r}.$ For every \(1 \le r \le \lfloor n/2 \rfloor\), that ratio is greater than 1, so the coefficients strictly increase as we move from the left edge toward the middle. After the midpoint, symmetry forces the same values to reappear in reverse order.

Therefore the first coefficient above the threshold in the left half completely determines every coefficient above the threshold in that row.

Turning the first crossing into a row count

Suppose \(r_0\) is the smallest index such that $\binom{n}{r_0} \gt T.$ By monotonicity up to the midpoint and symmetry afterward, every index $r_0 \le r \le n-r_0$ also satisfies \(\binom{n}{r} \gt T\). The number of such indices is $ (n-r_0)-r_0+1 = n+1-2r_0.$ This is the row contribution added by the implementations as soon as the first crossing is found.

If the scan reaches \(\lfloor n/2 \rfloor\) without exceeding \(T\), then the entire row stays at or below the threshold and contributes 0.

Worked Example: \(n=23\)

For \(n=23\), the recurrence produces $\binom{23}{1}=23,\ \binom{23}{2}=253,\ \binom{23}{3}=1771,\ \binom{23}{4}=8855,\ \binom{23}{5}=33649,$ $\binom{23}{6}=100947,\ \binom{23}{7}=245157,\ \binom{23}{8}=490314,\ \binom{23}{9}=817190,\ \binom{23}{10}=1144066.$ The first value above one million appears at \(r_0=10\). Symmetry then gives the matching values at \(r=11,12,13\), so the exceeding block is $r=10,11,12,13.$ Its size is $23+1-2\cdot 10=4.$ This is the same reasoning used for every row from 1 through 100.

How the Code Works

Inputs and checkpoint

The C++, Python, and Java implementations all work with two mathematical parameters: an upper row bound \(n_{\max}\) and a threshold \(T\). For the Project Euler problem they default to \(n_{\max}=100\) and \(T=10^6\). Each implementation also contains a small checkpoint: when \(n_{\max}=10\) and \(T=10\), the correct count is 25.

Single-value sweep across each row

For a fixed \(n\), the implementation starts with the coefficient \(1\) at \(r=0\). It then advances through $r=1,2,\dots,\left\lfloor \frac{n}{2}\right\rfloor,$ updating the running coefficient by $c \leftarrow c\frac{n-r+1}{r}.$ Because the update always lands on the exact next binomial coefficient, the loop never needs floating-point arithmetic or a precomputed table.

Early termination by symmetry

As soon as the running value exceeds the threshold, the implementation adds $n+1-2r$ to the total and immediately stops processing that row. This is correct because the first crossing identifies the entire central block above the threshold. For Problem 53, this also keeps the intermediate values modest: the computation breaks right after the first value above one million, so ordinary integer arithmetic is sufficient in all three languages.

Complexity Analysis

With upper bound \(N\), the loop performs at most $\sum_{n=1}^{N}\left\lfloor \frac{n}{2}\right\rfloor = O(N^2)$ coefficient updates. For \(N=100\), that worst-case total is only 2500 updates, and the real total is smaller because many rows stop before the midpoint.

The memory usage is \(O(1)\): the implementation stores only the current coefficient, the running answer, and a few counters and parameters. That matches the lightweight structure of the actual solution.

Footnotes and References

  1. Problem statement: Project Euler 53 - Combinatoric selections
  2. Binomial coefficient: Wikipedia - Binomial coefficient
  3. Pascal's triangle: Wikipedia - Pascal's triangle
  4. Combination: Wikipedia - Combination

Mathematical approach · C++ solution · Python solution · Java solution

Previous: Problem 52 · All Project Euler solutions · Next: Problem 54

Need help with a problem? Ask me! 💡
e
✦ Euler GLM 5.2
Hi! I'm Euler. Ask me anything about Project Euler problems, math concepts, or solution approaches! 🧮