All Euler problems
Project Euler

Almost Right-angled Triangles II

How many ordered triples (a,b,c) with a <= b <= c, a^2+b^2=c^2-1, a+b+c <= 75,000,000 exist?

Source sync May 21, 2026
Problem #0224
Level Level 12
Solved By 1,481
Languages C++, Python
Answer 4137330
Length 267 words
modular_arithmeticnumber_theorylinear_algebra

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Let us call an integer sided triangle with sides \(a \le b \le c\) barely obtuse if the sides satisfy

\(a^2 + b^2 = c^2 - 1\).

How many barely obtuse triangles are there with perimeter \(\le 75\,000\,000\)?

Problem 224: Almost Right-angled Triangles II

Mathematical Development

Write

Q(a,b,c)=a2+b2−c2.Q(a,b,c)=a^2+b^2-c^2.

We want the positive integer solutions of

Q(a,b,c)=−1.Q(a,b,c)=-1.

Any common divisor of a,b,ca,b,c would also divide

a2+b2−c2=−1,a^2+b^2-c^2=-1,

so every solution is primitive.

The classical Berggren matrices for the ternary quadratic form x2+y2−z2x^2+y^2-z^2 are

M1=(1−222−122−23),M2=(122212223),M3=(−122−212−223).M_1= \begin{pmatrix} 1&-2&2\\ 2&-1&2\\ 2&-2&3 \end{pmatrix}, \quad M_2= \begin{pmatrix} 1&2&2\\ 2&1&2\\ 2&2&3 \end{pmatrix}, \quad M_3= \begin{pmatrix} -1&2&2\\ -2&1&2\\ -2&2&3 \end{pmatrix}.

Each satisfies

Q(Miv)=Q(v),Q(M_i v)=Q(v),

so they preserve the equation Q=−1Q=-1.

The smallest positive solution is

(2,2,3),(2,2,3),

and the Hall-Berggren theory for this form shows that every positive solution of Q=−1Q=-1 is obtained by repeatedly applying these matrices.

Because aa and bb play symmetric roles, we sort them after each transformation. When a=ba=b, the first and third matrices produce the same sorted child, so only two distinct descendants remain. That gives a duplicate-free tree.

Therefore the counting problem is reduced to a depth-first traversal of that tree, pruning as soon as

a+b+c>75,000,000.a+b+c > 75{,}000{,}000.

Editorial

The direct factorization

(c−b)(c+b)=a2+1(c-b)(c+b)=a^2+1

is valid, but it is not the most convenient way to count all solutions up to a perimeter of seventy-five million. The better observation is that the equation lives on the quadratic surface

a2+b2−c2=−1,a^2+b^2-c^2=-1,

and that this surface has the same Berggren-style tree structure that ordinary Pythagorean triples have.

So instead of factoring 4m2+14m^2+1 for millions of values of mm, we start from the root (2,2,3)(2,2,3) and generate every solution exactly once. Each matrix application gives a new valid triple, sorting the first two coordinates restores the convention a≤ba \le b, and the perimeter bound cuts off the recursion naturally. That turns the problem into a pure tree walk with almost no arithmetic overhead.

Pseudocode

Store the three Berggren matrices that preserve a^2 + b^2 - c^2.

Initialize a stack with the root triple (2, 2, 3).
Set the answer counter to 0.

While the stack is not empty:
    remove one triple (a, b, c)
    reorder the first two entries so that a <= b

    If a + b + c exceeds the perimeter limit:
        discard this branch
        continue

    Count the current triple.

    If a = b:
        only the first two matrices give distinct sorted children
    otherwise:
        use all three matrices

    For each chosen matrix:
        compute the transformed triple
        keep it only if all three coordinates stay positive
        sort the first two coordinates
        push the child onto the stack

Return the counter.

Complexity Analysis

  • Time: Linear in the number of generated triples; every valid solution is visited once.
  • Space: Proportional to the maximum size of the DFS stack.

Answer

4137330\boxed{4137330}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_224/solution.cpp
#include <algorithm>
#include <array>
#include <iostream>
#include <tuple>
#include <vector>

using namespace std;

using Matrix = array<array<long long, 3>, 3>;

const Matrix matrices[3] = {
    {{{1, -2, 2}, {2, -1, 2}, {2, -2, 3}}},
    {{{1, 2, 2}, {2, 1, 2}, {2, 2, 3}}},
    {{{-1, 2, 2}, {-2, 1, 2}, {-2, 2, 3}}},
};

tuple<long long, long long, long long> transform(const Matrix& matrix,
                                                 long long a,
                                                 long long b,
                                                 long long c) {
    long long x = matrix[0][0] * a + matrix[0][1] * b + matrix[0][2] * c;
    long long y = matrix[1][0] * a + matrix[1][1] * b + matrix[1][2] * c;
    long long z = matrix[2][0] * a + matrix[2][1] * b + matrix[2][2] * c;
    return {x, y, z};
}

int main() {
    const long long limit = 75000000LL;
    long long count = 0;
    vector<tuple<long long, long long, long long>> stack = {{2, 2, 3}};

    while (!stack.empty()) {
        long long a0, b0, c;
        tie(a0, b0, c) = stack.back();
        stack.pop_back();

        long long a = min(a0, b0);
        long long b = max(a0, b0);

        if (a + b + c > limit) {
            continue;
        }

        count++;
        int childCount = (a == b ? 2 : 3);
        for (int i = 0; i < childCount; i++) {
            long long x0, y0, z;
            tie(x0, y0, z) = transform(matrices[i], a, b, c);
            if (x0 <= 0 || y0 <= 0 || z <= 0) {
                continue;
            }

            long long x = min(x0, y0);
            long long y = max(x0, y0);
            stack.push_back(make_tuple(x, y, z));
        }
    }

    cout << count << '\n';
    return 0;
}