Problem 540: Counting Primitive Pythagorean Triples
View on Project EulerProject Euler Problem 540 Solution
EulerSolve provides an optimized solution for Project Euler Problem 540, Counting Primitive Pythagorean Triples, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary We want the number of primitive Pythagorean triples \((a,b,c)\) with hypotenuse bound $$c \le N.$$ Euclid's parametrization says that every primitive triple appears uniquely in the form $$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$ with $$m>n>0,\qquad \gcd(m,n)=1,\qquad m\not\equiv n \pmod{2}.$$ So the problem is equivalent to counting parameter pairs \((m,n)\) satisfying those three conditions together with \(m^2+n^2\le N\). Mathematical Approach Let \(P(X)\) denote the number of primitive parameter pairs \((m,n)\) with \(m^2+n^2\le X\), \(m>n>0\), coprime, and of opposite parity. The desired answer is \(P(N)\). The implementations compute it in two layers: first a fast raw counter without the gcd condition, then a recurrence that removes non-primitive pairs. Step 1: Convert Primitive Triples into Parameter Pairs The classical parametrization gives a bijection between primitive triples and pairs \((m,n)\) such that $$m>n>0,\qquad \gcd(m,n)=1,\qquad m\not\equiv n \pmod{2}.$$ The hypotenuse becomes $$c=m^2+n^2,$$ so counting primitive triples with \(c\le N\) is exactly the same as counting those admissible pairs inside the quarter-circle $$m^2+n^2\le N.$$ This change of viewpoint is the foundation of the whole solution: geometry gives the bound, and arithmetic gives the primitivity conditions....
Detailed mathematical approach
Problem Summary
We want the number of primitive Pythagorean triples \((a,b,c)\) with hypotenuse bound
$$c \le N.$$
Euclid's parametrization says that every primitive triple appears uniquely in the form
$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$
with
$$m>n>0,\qquad \gcd(m,n)=1,\qquad m\not\equiv n \pmod{2}.$$
So the problem is equivalent to counting parameter pairs \((m,n)\) satisfying those three conditions together with \(m^2+n^2\le N\).
Mathematical Approach
Let \(P(X)\) denote the number of primitive parameter pairs \((m,n)\) with \(m^2+n^2\le X\), \(m>n>0\), coprime, and of opposite parity. The desired answer is \(P(N)\). The implementations compute it in two layers: first a fast raw counter without the gcd condition, then a recurrence that removes non-primitive pairs.
Step 1: Convert Primitive Triples into Parameter Pairs
The classical parametrization gives a bijection between primitive triples and pairs \((m,n)\) such that
$$m>n>0,\qquad \gcd(m,n)=1,\qquad m\not\equiv n \pmod{2}.$$
The hypotenuse becomes
$$c=m^2+n^2,$$
so counting primitive triples with \(c\le N\) is exactly the same as counting those admissible pairs inside the quarter-circle
$$m^2+n^2\le N.$$
This change of viewpoint is the foundation of the whole solution: geometry gives the bound, and arithmetic gives the primitivity conditions.
Step 2: Count Opposite-Parity Pairs Before Enforcing Coprimality
Define \(R(X)\) as the number of pairs \((m,n)\) with
$$m>n>0,\qquad m^2+n^2\le X,\qquad m\not\equiv n \pmod{2},$$
but without the condition \(\gcd(m,n)=1\).
For a fixed \(m\), the largest allowed \(n\) is
$$n_{\max}(m)=\min\left(m-1,\left\lfloor\sqrt{X-m^2}\right\rfloor\right).$$
The row contribution is therefore
$$r_X(m)=\begin{cases} \left\lfloor \dfrac{n_{\max}(m)}{2}\right\rfloor, & m\text{ odd},\\[6pt] \left\lfloor \dfrac{n_{\max}(m)+1}{2}\right\rfloor, & m\text{ even}. \end{cases}$$
Hence
$$R(X)=\sum_{m\ge 1} r_X(m).$$
The code evaluates this quickly by separating a full prefix of rows. If
$$m^2+(m-1)^2\le X,$$
then the entire interval \(1\le n\le m-1\) is inside the circle. Solving this inequality gives
$$m\le \frac{1+\sqrt{2X-1}}{2}.$$
Let
$$m_0=\left\lfloor\frac{1+\sqrt{2X-1}}{2}\right\rfloor,\qquad K=\left\lfloor\frac{m_0}{2}\right\rfloor.$$
The first \(2K\) rows contribute exactly
$$1+3+5+\cdots+(2K-1)=K^2,$$
because row \(2j-1\) contributes \(j-1\) and row \(2j\) contributes \(j\). After that prefix, the remaining upper bound \(\lfloor\sqrt{X-m^2}\rfloor\) decreases monotonically, so one moving square-root pointer is enough for the tail.
Step 3: Remove Non-Primitive Pairs by Their Odd Common Divisor
Now restore the coprimality condition. Opposite parity already rules out a factor \(2\), so any common divisor of \(m\) and \(n\) must be odd.
If \((m,n)\) has odd gcd \(d\), then
$$m=d\,m',\qquad n=d\,n',$$
and the reduced pair \((m',n')\) is primitive. The circle bound becomes
$$m'^2+n'^2\le \left\lfloor \frac{X}{d^2}\right\rfloor.$$
Therefore every raw pair is obtained uniquely from a primitive pair and an odd scaling factor, which gives
$$R(X)=\sum_{\substack{d\ge 1\\ d\equiv 1 \pmod{2}}} P\left(\left\lfloor \frac{X}{d^2}\right\rfloor\right).$$
Rearranging,
$$P(X)=R(X)-\sum_{\substack{d\ge 3\\ d\equiv 1 \pmod{2}}} P\left(\left\lfloor \frac{X}{d^2}\right\rfloor\right).$$
This is the recurrence used directly by the implementations. It is equivalent to odd-only Möbius inversion, but the code never needs an explicit table of Möbius values.
Step 4: Split the Recurrence at the Cube Root and Group Equal Quotients
Let
$$L=\left\lfloor N^{1/3}\right\rfloor.$$
First compute \(P(x)\) for every \(1\le x\le L\). For a fixed \(x\), odd divisors \(d\le x^{1/3}\) are handled one by one in
$$P(x)=R(x)-\sum_{\substack{d\ge 3\\ d\equiv 1 \pmod{2}}} P\left(\left\lfloor \frac{x}{d^2}\right\rfloor\right).$$
For larger \(d\), the quotient \(\left\lfloor x/d^2\right\rfloor\) is small, and many different \(d\) produce the same quotient. Those repeated values are grouped together. For a fixed small \(z\), the number of odd \(d\) with
$$\left\lfloor \frac{x}{d^2}\right\rfloor=z$$
is
$$\left\lfloor\frac{\sqrt{x/z}-1}{2}\right\rfloor-\left\lfloor\frac{\sqrt{x/(z+1)}-1}{2}\right\rfloor.$$
That converts many repeated recursive terms into one multiplication by a multiplicity.
After the small table is known, the implementations evaluate the transformed values
$$Q(t)=P\left(\left\lfloor \frac{N}{t^2}\right\rfloor\right),\qquad t\text{ odd},$$
processing odd \(t\) from large to small. When the recurrence for \(Q(t)\) reaches a reduced argument at most \(L\), it is read from the small table. Otherwise it is another transformed value \(Q(tu)\) with a larger odd factor, which has already been computed. Finally, the required answer is simply
$$Q(1)=P(N).$$
Worked Example: \(N=50\)
The opposite-parity pairs with \(m^2+n^2\le 50\) are
$$ (2,1),\ (3,2),\ (4,1),\ (4,3),\ (5,2),\ (5,4),\ (6,1),\ (6,3). $$
So \(R(50)=8\).
The only possible odd common divisor larger than \(1\) is \(3\), because \(5^2>50\). Thus
$$P(50)=R(50)-P\left(\left\lfloor\frac{50}{9}\right\rfloor\right)=8-P(5).$$
Now \(P(5)=1\), coming from the single pair \((2,1)\). Therefore
$$P(50)=8-1=7.$$
The seven primitive triples are
$$ (3,4,5),\ (5,12,13),\ (8,15,17),\ (7,24,25),\ (20,21,29),\ (12,35,37),\ (9,40,41). $$
This small example shows exactly how the raw geometric count and the odd-gcd recurrence fit together.
How the Code Works
The C++, Python, and Java implementations begin with exact integer square-root and cube-root routines so that every cutoff such as \(\lfloor \sqrt{x}\rfloor\) and \(\lfloor N^{1/3}\rfloor\) is computed safely despite floating-point starting estimates.
They then evaluate the raw counter \(R(X)\) by combining a closed-form prefix \(K^2\) with a monotone tail scan. The tail keeps one decreasing square-root boundary and alternates the parity formula for odd and even rows, which avoids recomputing the whole range of \(n\) for each \(m\).
Next the implementation fills a first table for all primitive counts \(P(x)\) with \(x\le L\). Each entry uses the recurrence above, split into individually handled large quotients and grouped small quotients.
After that it fills a second table for transformed arguments \(\left\lfloor N/t^2\right\rfloor\) with odd \(t\), processed in descending order. Small reduced arguments are read from the first table; large reduced arguments correspond to already computed transformed values with larger odd factors.
The entry for \(t=1\) is \(P(N)\), so it is exactly the number of primitive Pythagorean triples with hypotenuse at most \(N\).
Complexity Analysis
A single evaluation of the raw counter \(R(X)\) costs \(O(\sqrt{X})\) time because the tail scan visits only the remaining boundary rows. The recurrence work around that raw counter contributes about \(O(X^{1/3})\) grouped-divisor operations.
Building the small table up to \(L=\lfloor N^{1/3}\rfloor\) costs \(O(N^{1/2})\) time. In the transformed stage, the dominant raw-count cost is
$$\sum_{\substack{t\le L\\ t\equiv 1 \pmod{2}}} O\left(\sqrt{\frac{N}{t^2}}\right)=O(\sqrt{N}\log N).$$
The extra grouped-divisor work is smaller than that dominant term. The memory usage is \(O(N^{1/3})\), because both stored tables have size proportional to the cube-root threshold.
Footnotes and References
- Problem page: https://projecteuler.net/problem=540
- Pythagorean triple: Wikipedia — Pythagorean triple
- Coprime integers: Wikipedia — Coprime integers
- Möbius inversion formula: Wikipedia — Möbius inversion formula
- Möbius function: Wikipedia — Möbius function
Mathematical approach · C++ solution · Python solution · Java solution
Previous: Problem 539 · All Project Euler solutions · Next: Problem 541