Skip to content
← All posts

Count Square Sum Triples: Enumerating Pythagorean Triples

4 min read
leetcodeproblemeasymath

LeetCode 1925: Count Square Sum Triples gives you a positive integer n and asks you to count the number of square triples (a, b, c) where a * a + b * b == c * c and 1 <= a, b, c <= n. If you have seen Pythagorean triples before, this is exactly that, but you need to count every ordered pair, not just the unique triangles. That means (3, 4, 5) and (4, 3, 5) are two separate triples.

The constraint is small (n <= 250), so a brute-force enumeration of all pairs (a, b) works perfectly. For each pair, you compute a*a + b*b, check whether the result is a perfect square, and verify the root is at most n. That is the entire algorithm.

Square sum triples for n = 10abca² + b² = c²#13+4=53²+4²=25=5²#24+3=54²+3²=25=5²#36+8=106²+8²=100=10²#48+6=108²+6²=100=10²count = 4
For n = 10, there are 4 square sum triples. Note that (3, 4, 5) and (4, 3, 5) are counted separately since a and b are in different positions.

The approach

The plan is to iterate over every pair (a, b) where 1 <= a <= n and 1 <= b <= n. For each pair:

  1. Compute c_sq = a * a + b * b.
  2. Compute c = isqrt(c_sq) (the integer square root).
  3. If c * c == c_sq (meaning c_sq is a perfect square) and c <= n, you have a valid triple. Increment your counter.

Why does step 3 work? The integer square root isqrt(c_sq) gives you the largest integer whose square is at most c_sq. If that integer, when squared, gives you exactly c_sq back, then c_sq is a perfect square. Otherwise it is not.

You do not need to enumerate c as a third loop variable. Computing it directly from a and b keeps the solution at O(n^2) instead of O(n^3).

The code

import math

def countTriples(n: int) -> int:
    count = 0
    for a in range(1, n + 1):
        for b in range(1, n + 1):
            c_sq = a * a + b * b
            c = math.isqrt(c_sq)
            if c * c == c_sq and c <= n:
                count += 1
    return count

The two nested loops cover all ordered pairs. Inside, math.isqrt gives the integer square root in O(1) time, and the perfect-square check is a single comparison. No floating-point rounding issues, no epsilon comparisons. The isqrt function is exact.

One thing to note: you could also use int(math.sqrt(c_sq)) instead of math.isqrt, but math.isqrt is preferred because it avoids floating-point precision problems. For large values of c_sq, math.sqrt can return a float that rounds incorrectly, while math.isqrt always returns the exact integer floor.

Step-by-step walkthrough

Let's trace through n = 10. Most pairs will not produce a perfect square, so we will focus on the interesting iterations.

Tracing through countTriples(10), showing key iterations:

Iteration: a=1, b=1

a = 1b = 1
c_sq =1² + 1² = 1 + 1 = 2
sqrt(2) =1.41...not a perfect square, skip
count =0

1 + 1 = 2, which is not a perfect square. No triple here.

Iteration: a=1, b=2

a = 1b = 2
c_sq =1² + 2² = 1 + 4 = 5
sqrt(5) =2.23...not a perfect square, skip
count =0

1 + 4 = 5, which is not a perfect square. Move on.

Iteration: a=3, b=4

a = 3b = 4
c_sq =3² + 4² = 9 + 16 = 25
sqrt(25) =5perfect square, valid triple
count =1

9 + 16 = 25. sqrt(25) = 5, and 5 is within n = 10. Valid triple (3, 4, 5). Increment count.

Iteration: a=4, b=3

a = 4b = 3
c_sq =4² + 3² = 16 + 9 = 25
sqrt(25) =5perfect square, valid triple
count =2

16 + 9 = 25. sqrt(25) = 5. Valid triple (4, 3, 5). Order matters, so this counts separately.

Iteration: a=6, b=8

a = 6b = 8
c_sq =6² + 8² = 36 + 64 = 100
sqrt(100) =10perfect square, valid triple
count =3

36 + 64 = 100. sqrt(100) = 10, which equals n. Valid triple (6, 8, 10). Increment count.

Iteration: a=8, b=6

a = 8b = 6
c_sq =8² + 6² = 64 + 36 = 100
sqrt(100) =10perfect square, valid triple
count =4

64 + 36 = 100. sqrt(100) = 10. Valid triple (8, 6, 10). After all iterations, count = 4.

All pairs checked. Return count = 4.

The four valid triples for n = 10 are (3,4,5), (4,3,5), (6,8,10), and (8,6,10).

Complexity

TimeSpace
Pair enumerationO(n^2)O(1)

Time: O(n^2). Two nested loops from 1 to n. Inside each iteration, the work is O(1): one multiplication, one isqrt call, one comparison.

Space: O(1). Only a handful of integer variables. No auxiliary arrays or data structures.

With n <= 250, the inner loop runs at most 62,500 times. That finishes in under a millisecond on any modern machine.

Building blocks

This problem decomposes into two reusable pieces.

1. Exhaustive pair enumeration

When the search space is small enough, iterating over all pairs is a valid and often the simplest approach. Two nested loops over the range [1, n] give you every ordered pair (a, b). You will see this same enumeration pattern in problems like counting pairs with a given sum, finding matching elements, or brute-force search on small inputs.

for a in range(1, n + 1):
    for b in range(1, n + 1):
        # check some condition on (a, b)

2. Perfect square check with integer square root

To test whether a number x is a perfect square without floating-point rounding errors, compute c = isqrt(x) and verify c * c == x. This micro-pattern shows up in problems like Valid Perfect Square and Sum of Square Numbers. It is a reliable building block whenever you need exact square-root testing.

c = math.isqrt(x)
if c * c == x:
    # x is a perfect square

Edge cases

  • n = 1: No triple exists because the smallest Pythagorean triple is (3, 4, 5). Both loops run once with a = 1, b = 1, giving c_sq = 2, which is not a perfect square. Returns 0.
  • n = 4: Still no valid triples. The smallest c value is 5, which exceeds n = 4. Returns 0.
  • n = 5: The first triples appear: (3, 4, 5) and (4, 3, 5). Returns 2.
  • n = 10: Four triples total. In addition to the two from n = 5, you also get (6, 8, 10) and (8, 6, 10) since c = 10 is now within range.

From understanding to recall

The algorithm is short: two loops, one square root, one comparison. But when you sit down to code it from scratch, the small decisions matter. Do you use math.isqrt or math.sqrt? Is the check c * c == c_sq or c_sq == c * c? Do you need c <= n or c < n?

These details are easy to second-guess under pressure. Spaced repetition removes the doubt. After writing the solution a few times at increasing intervals, the pattern of "enumerate pairs, compute sum of squares, check perfect square, verify range" becomes automatic. You stop thinking about whether isqrt rounds down or up because you have verified it yourself multiple times.

The key insight to lock in is: you do not need a third loop for c. Computing c directly from a and b is the move that keeps the solution efficient and clean.

Related posts