Singular Integer Right Triangles
Given that L is the perimeter of a right triangle with integer sides, for how many values of L <= 1,500,000 can exactly one integer-sided right triangle be formed?
Problem Statement
This archive keeps the full statement, math, and original media on the page.
It turns out that $12 \operatorname{cm}$ is the smallest length of wire that can be bent to form an integer sided right angle triangle in exactly one way, but there are many more examples. \begin{align*} 12 \operatorname{cm}: (3, 4, 5) \\ 24 \operatorname{cm}: (6, 8, 10) \\ 30 \operatorname{cm}: (5, 12, 13) \\ 36 \operatorname{cm}: (9, 12, 15) \\ 40 \operatorname{cm}: (8, 15, 17) \\ 48 \operatorname{cm}: (12, 16, 20) \\ \end{align*} In contrast, some lengths of wire, like $20 \operatorname{cm}$, cannot be bent to form an integer sided right angle triangle, and other lengths allow more than one solution to be found; for example, using $120 \operatorname{cm}$ it is possible to form exactly three different integer sided right angle triangles. $$120 \operatorname{cm}: (30, 40, 50), (20, 48, 52), (24, 45, 51)$$ Given that $L$ is the length of the wire, for how many values of $L \le 1\,500\,000$ can exactly one integer sided right angle triangle be formed?
Problem 75: Singular Integer Right Triangles
Mathematical Analysis
Theorem 1 (Euclid’s Parametrization of Primitive Triples)
Every primitive Pythagorean triple with , , and can be written (up to swapping and ) as:
where , , and . Conversely, every such pair generates a primitive triple.
Proof.
Given valid , direct computation verifies:
For primitivity: suppose a prime divides . Then and . Since and have opposite parity, is odd, so . Then from and odd, or . If , then from we get , hence , contradicting . Similarly for . Thus , and since implies (as ), the triple is primitive.
Let be a primitive triple with even (exactly one of must be even; if both were odd, then , which is impossible since squares are or ). Factor:
Since is odd and is odd (as with odd and even), both and are even. Write , , so and .
Claim: . Indeed, if and , then and , so . Since (because and any common factor of would divide hence ), we get .
Since is a perfect square and , both and must be perfect squares: , . Then , , , with (since ), (from ), and (since is odd).
Theorem 2 (Perimeter Formula)
The perimeter of the primitive triple generated by is .
Proof.
Theorem 3 (Scaling Principle)
Every Pythagorean triple is uniquely expressible as for a positive integer and a primitive triple . Consequently, perimeter admits a Pythagorean triple if and only if for some primitive perimeter and some .
Proof. Setting , the triple is primitive and . Uniqueness: if with both primitive, then (since ) and the triples coincide.
Lemma (Bound on )
For primitive perimeters with , we have .
Proof. From and : , so . For , this gives , i.e., .
Definition (Singular Perimeter)
A perimeter is called singular if there is exactly one Pythagorean triple (counting different orderings of legs as the same triple) with perimeter .
Theorem 4 (Counting Method)
For each perimeter , define as the number of distinct Pythagorean triples with perimeter . Then . The answer is .
Proof. By Theorem 3, each Pythagorean triple with perimeter corresponds to a unique factorization with primitive and . Distinct primitive perimeters dividing give distinct triples.
Editorial
Euclid’s parametrization gives a clean generation scheme for all primitive triples. Every valid pair with opposite parity and produces exactly one primitive perimeter
That already handles candidate generation. The filtering comes from the Euclid conditions: pairs with the same parity or a common factor do not produce primitive triples, so we discard them immediately. Once a primitive perimeter is known, every multiple of it corresponds to a non-primitive triple, so we mark all multiples in a counter array. After the sweep, a perimeter is singular precisely when its counter is equal to one.
Pseudocode
Create a counter array for all perimeters up to the limit.
Compute the largest necessary value of m.
For each m starting from 2:
For each n with 1 <= n < m:
If m and n have the same parity, skip this pair
If gcd(m, n) is greater than 1, skip this pair
Compute the primitive perimeter L0 = 2m(m + n)
If L0 is above the limit, stop increasing n for this m
For every multiple of L0 within the limit:
increase the corresponding perimeter counter
Count how many perimeters were hit exactly once.
Return that count.
Complexity Analysis
Outer loop: The iterations over pairs total before filtering.
Inner loop: For each primitive perimeter , we mark multiples. The total work is:
By analogy with the sum of reciprocals over a sieved set, this is .
- Time: .
- Space: for the counter array.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
// Count singular perimeters L <= 1,500,000.
// Generate primitive Pythagorean triples via Euclid: L0 = 2m(m+n).
// Mark all multiples kL0. Count perimeters hit exactly once.
const int LMAX = 1500000;
vector<int> cnt(LMAX + 1, 0);
int mlimit = (int)sqrt((double)LMAX / 2.0) + 1;
for (int m = 2; m <= mlimit; m++) {
for (int n = 1; n < m; n++) {
if ((m - n) % 2 == 0) continue;
if (__gcd(m, n) != 1) continue;
int L0 = 2 * m * (m + n);
if (L0 > LMAX) break;
for (int L = L0; L <= LMAX; L += L0) {
cnt[L]++;
}
}
}
int answer = 0;
for (int L = 1; L <= LMAX; L++) {
if (cnt[L] == 1) answer++;
}
cout << answer << endl;
return 0;
}
"""
Project Euler Problem 75: Singular Integer Right Triangles
Count values of L <= 1,500,000 for which exactly one integer-sided
right triangle can be formed with perimeter L.
By Theorems 1-3, generate all primitive Pythagorean triple perimeters
via Euclid's parametrization L0 = 2m(m+n), then count multiples.
A perimeter is "singular" iff it is hit exactly once.
"""
from math import gcd, isqrt
def solve():
LMAX = 1_500_000
cnt = [0] * (LMAX + 1)
mlimit = isqrt(LMAX // 2) + 1
for m in range(2, mlimit + 1):
for n in range(1, m):
if (m - n) % 2 == 0:
continue
if gcd(m, n) != 1:
continue
L0 = 2 * m * (m + n)
if L0 > LMAX:
break
for L in range(L0, LMAX + 1, L0):
cnt[L] += 1
return sum(1 for L in range(1, LMAX + 1) if cnt[L] == 1)
if __name__ == "__main__":
answer = solve()
assert answer == 161667
print(answer)