Problem 39: Integer Right Triangles
View on Project EulerProject Euler Problem 39 Solution
EulerSolve provides an optimized solution for Project Euler Problem 39, Integer Right Triangles, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary We are looking for the perimeter \(p \le 1000\) that produces the largest number of integer right triangles. A valid solution is a triple \((a,b,c)\) with \(1 \le a \le b \lt c\), satisfying $a^2+b^2=c^2,\qquad a+b+c=p.$ The C++, Python, and Java implementations all solve this by scanning the finite set of possible leg pairs, detecting when the hypotenuse is integral, counting how many times each perimeter occurs, and then returning the perimeter whose count is largest. Mathematical Approach The central idea is that we do not need a separate search for each perimeter. Instead, we can enumerate all possible ordered pairs of legs that could appear in any triangle with perimeter at most 1000, and let each valid triangle contribute to the counter of its own perimeter. Bounding the Search Region Order the two legs so that \(a \le b\)....
Detailed mathematical approach
Problem Summary
We are looking for the perimeter \(p \le 1000\) that produces the largest number of integer right triangles. A valid solution is a triple \((a,b,c)\) with \(1 \le a \le b \lt c\), satisfying
$a^2+b^2=c^2,\qquad a+b+c=p.$
The C++, Python, and Java implementations all solve this by scanning the finite set of possible leg pairs, detecting when the hypotenuse is integral, counting how many times each perimeter occurs, and then returning the perimeter whose count is largest.
Mathematical Approach
The central idea is that we do not need a separate search for each perimeter. Instead, we can enumerate all possible ordered pairs of legs that could appear in any triangle with perimeter at most 1000, and let each valid triangle contribute to the counter of its own perimeter.
Bounding the Search Region
Order the two legs so that \(a \le b\). Since \(a\) is then the smallest side and the perimeter is at most 1000, we have
$3a \le a+b+c \le 1000,$
so necessarily
$1 \le a \le \left\lfloor \frac{1000}{3} \right\rfloor.$
Once \(a\) is fixed, the inequality \(b \le c\) gives
$a+2b \le a+b+c \le 1000,$
hence
$a \le b \le \left\lfloor \frac{1000-a}{2} \right\rfloor.$
So every admissible triangle must lie in the finite lattice region
$1 \le a \le \left\lfloor \frac{1000}{3} \right\rfloor,\qquad a \le b \le \left\lfloor \frac{1000-a}{2} \right\rfloor.$
Those are exactly the loop bounds used by the implementations.
Recovering the Hypotenuse from the Legs
For a chosen pair \((a,b)\), the Pythagorean condition determines the hypotenuse completely:
$c^2=a^2+b^2,\qquad c=\sqrt{a^2+b^2}.$
Therefore the pair \((a,b)\) produces an integer right triangle if and only if \(a^2+b^2\) is a perfect square. This is the key mathematical test in the code. The implementations compute the integer square root of \(a^2+b^2\) and accept the candidate exactly when squaring that root recovers the original value.
If the test fails, the pair contributes nothing. If it succeeds, the triangle is forced: there is one and only one integer value of \(c\) attached to that pair.
Why Counting by Perimeter Is Enough
Whenever \(c\) is integral, the perimeter is simply
$p=a+b+c.$
Instead of solving a separate Diophantine problem for each \(p\), we maintain a counter for every possible perimeter from 0 through 1000. Each valid triple increments the counter belonging to its own perimeter. After all candidate pairs have been tested, the answer is the perimeter whose counter is maximal.
This converts the original question into a histogram problem: scan once through the bounded search region, classify every valid triangle by its perimeter, then choose the busiest bucket.
Why the Enumeration Is Complete and Duplication-Free
The ordering \(a \le b\) prevents the same triangle from being counted twice as \((a,b,c)\) and \((b,a,c)\). Also, once \(a\) and \(b\) are positive, the inequality \(c \gt b\) is automatic because
$c^2=a^2+b^2>b^2.$
Conversely, every integer right triangle with perimeter at most 1000 satisfies the derived bounds on \(a\) and \(b\), so its ordered leg pair must appear somewhere in the scan. That proves two things at once:
no valid triangle is missed, and no valid triangle is counted twice.
Worked Example: Perimeter 120
The perimeter \(120\) is a useful checkpoint because it has exactly three integer right-triangle solutions. In the search region, the relevant leg pairs are
$ (20,48),\qquad (24,45),\qquad (30,40). $
For these pairs, the perfect-square test succeeds:
$20^2+48^2=52^2,\qquad 24^2+45^2=51^2,\qquad 30^2+40^2=50^2.$
Each one contributes once to the counter for
$p=20+48+52=24+45+51=30+40+50=120.$
No other ordered pair in the allowed region produces perimeter 120, so the count for \(p=120\) is 3. That is why the implementations use 120 as a checkpoint input.
How the Code Works
The C++, Python, and Java implementations all follow the same structure. First they allocate an array indexed by perimeter; each entry stores how many valid right triangles have been found for that perimeter. Then they run the two nested loops over the bounded region for \((a,b)\).
For each candidate pair, the implementation forms \(a^2+b^2\), takes its integer square root, and checks whether the square-root test is exact. If it is, the code computes the corresponding perimeter \(p=a+b+c\) and increments that perimeter's counter, provided \(p\) is still within the requested bound.
After enumeration is complete, the implementation scans the counter array from left to right and keeps the perimeter with the largest count seen so far. Because the update happens only when a strictly larger count appears, ties are resolved in favor of the first perimeter reaching that maximum. The shared checkpoint verifies that when the upper bound is 120, the returned perimeter is 120.
Complexity Analysis
If the perimeter bound is written as \(L\), the algorithm examines all integer pairs in
$1 \le a \le \left\lfloor \frac{L}{3} \right\rfloor,\qquad a \le b \le \left\lfloor \frac{L-a}{2} \right\rfloor.$
The number of tested pairs is therefore
$\sum_{a=1}^{\lfloor L/3 \rfloor}\left(\left\lfloor \frac{L-a}{2} \right\rfloor-a+1\right)=O(L^2).$
For \(L=1000\), that is about \(8.3\times 10^4\) candidate pairs, which is small. Each iteration performs constant-size arithmetic together with one integer square-root computation, and the final scan over the perimeter counters adds only \(O(L)\) time. Memory usage is \(O(L)\) because the only growing data structure is the perimeter-count array.
Footnotes and References
- Project Euler problem page: https://projecteuler.net/problem=39
- Pythagorean triple: Wikipedia - Pythagorean triple
- Pythagorean theorem: Wikipedia - Pythagorean theorem
- Perfect square: Wikipedia - Square number
- Euclid's formula, as classical background on generating triples: Wikipedia - Generating a Pythagorean triple