Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

Complete solutions in C++, Python & Java — with step-by-step mathematical explanations

All Problems

Problem 299: Three Similar Triangles

View on Project Euler

Project Euler Problem 299 Solution

EulerSolve provides an optimized solution for Project Euler Problem 299, Three Similar Triangles, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary The problem starts with $A(a,0),\qquad B(b,0),\qquad C(0,a),\qquad D(0,d),$ where \(0<a<b\) and \(0<a<d\). We seek integer triplets \((a,b,d)\) for which there exists an integer point \(P\) on \(AC\) such that the three triangles \(ABP\), \(CDP\), and \(BDP\) are all similar. The quantity to count is the number of such triplets with $b+d<N.$ Mathematical Approach 1. Why \(a=c\) and why two cases appear Project Euler already states that similarity is possible only when \(a=c\), so the segment \(AC\) lies on the line $x+y=a.$ Because \(OA\) and \(OC\) are symmetric, the angles at \(A\) and \(C\) are both \(135^\circ\). Matching the three similar triangles leaves two possible angle correspondences: Incenter case. The lines \(PB\) and \(PD\) are angle bisectors of the right triangle \(OBD\), so \(P\) is the incenter of \(\triangle OBD\). Parallel case. The line \(AC\) is parallel to \(BD\). These two families are disjoint, and the code counts them separately. 2. Incenter case: reduction to a Pythagorean triple If \(P\) is the incenter of the right triangle with legs \(b\) and \(d\), then its coordinates must be $P=(i,i),$ where \(i\) is the inradius. Since \(P\in AC\), we have $a=2i.$ For a right triangle, the inradius is $i=\frac{b+d-\sqrt{b^2+d^2}}{2}.$ Therefore \(a\) is integral exactly when \(\sqrt{b^2+d^2}\) is integral....

Detailed mathematical approach

Problem Summary

The problem starts with

$A(a,0),\qquad B(b,0),\qquad C(0,a),\qquad D(0,d),$

where \(0<a<b\) and \(0<a<d\). We seek integer triplets \((a,b,d)\) for which there exists an integer point \(P\) on \(AC\) such that the three triangles \(ABP\), \(CDP\), and \(BDP\) are all similar. The quantity to count is the number of such triplets with

$b+d<N.$

Mathematical Approach

1. Why \(a=c\) and why two cases appear

Project Euler already states that similarity is possible only when \(a=c\), so the segment \(AC\) lies on the line

$x+y=a.$

Because \(OA\) and \(OC\) are symmetric, the angles at \(A\) and \(C\) are both \(135^\circ\). Matching the three similar triangles leaves two possible angle correspondences:

Incenter case. The lines \(PB\) and \(PD\) are angle bisectors of the right triangle \(OBD\), so \(P\) is the incenter of \(\triangle OBD\).

Parallel case. The line \(AC\) is parallel to \(BD\).

These two families are disjoint, and the code counts them separately.

2. Incenter case: reduction to a Pythagorean triple

If \(P\) is the incenter of the right triangle with legs \(b\) and \(d\), then its coordinates must be

$P=(i,i),$

where \(i\) is the inradius. Since \(P\in AC\), we have

$a=2i.$

For a right triangle, the inradius is

$i=\frac{b+d-\sqrt{b^2+d^2}}{2}.$

Therefore \(a\) is integral exactly when \(\sqrt{b^2+d^2}\) is integral. So the incenter family is equivalent to counting right triangles with integer legs \(b,d\) and hypotenuse

$z=\sqrt{b^2+d^2},$

under the limit \(b+d<N\).

The sample \((a,b,d)=(2,3,4)\) comes from the primitive Pythagorean triple

$3^2+4^2=5^2,$

because

$a=b+d-z=3+4-5=2,$

and then \(P=(1,1)\) lies on \(x+y=2\).

3. Why the code uses \(s_1\) and \(s_2\)

Write

$u=b-a,\qquad v=d-a.$

Then

$b=a+u,\qquad d=a+v,\qquad z=b+d-a=a+u+v.$

Substituting \(z^2=b^2+d^2\) gives

$a^2=2uv.$

So the primitive solutions come in two orientations:

$u=g m^2,\qquad v=2g n^2,\qquad a=2gmn,$

or

$u=2g m^2,\qquad v=g n^2,\qquad a=2gmn,$

with \(\gcd(m,n)=1\). After reconstructing \(b\) and \(d\), the perimeter bound becomes

$b+d=g(m^2+4mn+2n^2)$

in the first orientation, and

$b+d=g(2m^2+4mn+n^2)$

in the second. These are exactly the denominators

$s_1=m^2+4mn+2n^2,\qquad s_2=2m^2+4mn+n^2$

used by the code. For fixed primitive \((m,n)\), all scaled copies are obtained by multiplying by \(g\), so the number of valid scales is

$\left\lfloor\frac{N-1}{s_1}\right\rfloor \quad \text{or} \quad \left\lfloor\frac{N-1}{s_2}\right\rfloor.$

The parity filters in the implementation are the primitive-normalization rules that prevent duplicate descriptions.

4. Parallel case: reduction to \(Q^2+2f^2=a^2\)

In the second similarity pattern we have

$AC\parallel BD,$

so the slope condition forces

$d=b.$

Let

$f=b-a>0.$

Then \(D=(0,b)\), \(B=(b,0)\), and the circumcenter of \(\triangle BDP\) is \(X=(b,b)\). Hence \(P\) must lie on the circle

$ (x-b)^2+(y-b)^2=b^2,$

and also on the line \(AC\), namely

$y=a-x.$

Substituting gives

$x=\frac{a\pm \sqrt{a^2-2f^2}}{2}.$

Therefore an integer point \(P\) exists exactly when

$Q^2=a^2-2f^2,$

that is,

$Q^2+2f^2=a^2.$

A small example is

$a=3,\qquad f=2,\qquad b=d=5,$

because \(1^2+2\cdot 2^2=3^2\), giving the triplet \((3,5,5)\).

5. Why the code uses \(s_3\)

Primitive solutions of

$Q^2+2f^2=a^2$

are parameterized by coprime integers \(p,q\) with odd \(p\):

$Q=p^2-2q^2,\qquad f=2pq,\qquad a=p^2+2q^2.$

After scaling by \(g\), we get

$b=d=a+f=g(p^2+2pq+2q^2).$

Hence the bound \(b+d<N\) becomes

$2g(p^2+2pq+2q^2)<N,$

so for each primitive pair the number of scales is

$\left\lfloor\frac{N-1}{2(p^2+2pq+2q^2)}\right\rfloor.$

This is exactly the denominator

$s_3=2(p^2+2pq+2q^2)$

used in the implementation.

6. Final counting formula and checkpoints

The two families are disjoint, so the total is

$T(N)=\sum \left\lfloor\frac{N-1}{s_1}\right\rfloor+\sum \left\lfloor\frac{N-1}{s_2}\right\rfloor+\sum \left\lfloor\frac{N-1}{s_3}\right\rfloor.$

The code validates itself with the published checkpoints

$T(100)=92,\qquad T(100000)=320471.$

How the Code Works

count_family_x_eq_y(...) enumerates the incenter family via the two forms \(s_1,s_2\). count_family_u_eq_v(...) enumerates the parallel family via \(s_3\). For each primitive parameter pair, the contribution is the number of scale factors \(g\) allowed by \(b+d<N\). The final answer is the sum of both family totals.

Complexity Analysis

The loops over \((m,n)\) and \((p,q)\) break as soon as the corresponding denominator reaches \(N\). So the practical runtime is far below a naive scan over all triples \((a,b,d)\). Memory usage is \(O(1)\) beyond a few counters, and the C++ / Python versions optionally split independent parameter ranges across threads or processes.

Further Reading

  1. Problem page: https://projecteuler.net/problem=299
  2. Pythagorean triples: https://en.wikipedia.org/wiki/Pythagorean_triple
  3. Pell-type forms \(x^2+2y^2=z^2\): https://en.wikipedia.org/wiki/Pell%27s_equation

Mathematical approach · C++ solution · Python solution · Java solution

Previous: Problem 298 · All Project Euler solutions · Next: Problem 300

Need help with a problem? Ask me! 💡
e
✦ Euler GLM 5.2
Hi! I'm Euler. Ask me anything about Project Euler problems, math concepts, or solution approaches! 🧮