Problem 962: Angular Bisector and Tangent 2
View on Project EulerProject Euler Problem 962 Solution
EulerSolve provides an optimized solution for Project Euler Problem 962, Angular Bisector and Tangent 2, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary Problem 962 asks for all integer triples \((a,b,c)\) with \(a \le b \le c \lt a+b\) and \(a+b+c \le N\) for which the geometric construction in the statement leads to \[ Q=\frac{a^3(a+b-c)(a+b+c)}{b(a+b)^2} \] being an integer perfect square. For the Project Euler instance, \(N=10^6\). A direct sweep over all triangles is far too slow, so the solution replaces the \((a,b,c)\)-search by a number-theoretic count over a much smaller parameter space. Mathematical Approach The key step is to separate the common scale of \(a\) and \(b\), prove that every valid triangle has a rigid difference-of-squares form, and then count the remaining possibilities through squarefree data and interval arithmetic. Separating the common scale of \(a\) and \(b\) Let \[ g=\gcd(a,b), \qquad a=ug, \qquad b=vg, \] with \(\gcd(u,v)=1\) and \(u \le v\). Write \[ k=u+v. \] Then \(a+b=kg\), so the square test becomes \[ Q=\frac{u^3\bigl(k^2g^2-c^2\bigr)}{vk^2}. \] This already shows why the reduced ratio \(u:v\) is the real state space: once \(u\) and \(v\) are fixed, the remaining freedom sits inside \(g\) and \(c\). Why \(c\) must be a multiple of \(k\) If \(Q\) is an integer, then \(k^2\) divides the numerator \(u^3(k^2g^2-c^2)\). Because \(\gcd(u,k)=1\), the factor \(u^3\) is irrelevant for divisibility by \(k^2\), so \[ k^2 \mid \bigl(k^2g^2-c^2\bigr)....
Detailed mathematical approach
Problem Summary
Problem 962 asks for all integer triples \((a,b,c)\) with \(a \le b \le c \lt a+b\) and \(a+b+c \le N\) for which the geometric construction in the statement leads to
\[ Q=\frac{a^3(a+b-c)(a+b+c)}{b(a+b)^2} \]
being an integer perfect square. For the Project Euler instance, \(N=10^6\). A direct sweep over all triangles is far too slow, so the solution replaces the \((a,b,c)\)-search by a number-theoretic count over a much smaller parameter space.
Mathematical Approach
The key step is to separate the common scale of \(a\) and \(b\), prove that every valid triangle has a rigid difference-of-squares form, and then count the remaining possibilities through squarefree data and interval arithmetic.
Separating the common scale of \(a\) and \(b\)
Let
\[ g=\gcd(a,b), \qquad a=ug, \qquad b=vg, \]
with \(\gcd(u,v)=1\) and \(u \le v\). Write
\[ k=u+v. \]
Then \(a+b=kg\), so the square test becomes
\[ Q=\frac{u^3\bigl(k^2g^2-c^2\bigr)}{vk^2}. \]
This already shows why the reduced ratio \(u:v\) is the real state space: once \(u\) and \(v\) are fixed, the remaining freedom sits inside \(g\) and \(c\).
Why \(c\) must be a multiple of \(k\)
If \(Q\) is an integer, then \(k^2\) divides the numerator \(u^3(k^2g^2-c^2)\). Because \(\gcd(u,k)=1\), the factor \(u^3\) is irrelevant for divisibility by \(k^2\), so
\[ k^2 \mid \bigl(k^2g^2-c^2\bigr). \]
The term \(k^2g^2\) is already a multiple of \(k^2\), hence \(k^2 \mid c^2\), which implies
\[ k \mid c. \]
So every valid triangle can be written as
\[ c=kt \]
for some integer \(t\) with \(0 \lt t \lt g\). Substituting this into the previous formula collapses the \(k^2\)-denominator:
\[ Q=\frac{u^3(g^2-t^2)}{v}. \]
From the triangle to the pair \((r,s)\)
Now factor the difference of squares by setting
\[ r=g-t, \qquad s=g+t. \]
Then
\[ g=\frac{r+s}{2}, \qquad t=\frac{s-r}{2}, \]
so \(r\) and \(s\) are positive integers with \(r \lt s\) and
\[ r \equiv s \pmod 2. \]
The triangle sides become
\[ a=\frac{u(r+s)}{2}, \qquad b=\frac{v(r+s)}{2}, \qquad c=\frac{k(s-r)}{2}, \]
and the perimeter is especially simple:
\[ a+b+c=ks. \]
The square test is now
\[ Q=\frac{u^3rs}{v}. \]
This is the form used by the optimized algorithm. Instead of iterating over \(c\), it counts admissible pairs \((r,s)\) for each reduced ratio \(u:v\).
Turning the square condition into squarefree data
Write
\[ u=d\,h^2, \]
where \(d=\operatorname{sf}(u)\) is the squarefree part of \(u\). Then
\[ u^3=d^3h^6=d^2(dh^3)^2, \]
so \(u^3\) contributes one squarefree copy of \(d\) and everything else is already a square. Therefore
\[ Q \text{ is a perfect square } \Longleftrightarrow \frac{drs}{v} \text{ is a perfect square.} \]
That is why the implementations precompute the squarefree kernel of \(u\). For primes dividing \(v\), the product \(rs\) must supply enough exponent to cancel the denominator and leave an even valuation. For primes dividing \(d\), the product \(rs\) must carry an odd valuation. These prime-by-prime requirements are compressed into a smallest admissible base value \(s_0\), and every valid \(s\) for fixed \((u,v,r)\) has the form
\[ s=s_0n^2 \]
with \(n \ge 1\).
Bounds coming from the triangle ordering and the perimeter cap
Let
\[ L=\left\lfloor\frac{N}{k}\right\rfloor. \]
Since the perimeter equals \(ks\), we must have \(s \le L\). The remaining geometric restriction is \(c \ge b\), which becomes
\[ \frac{k(s-r)}{2} \ge \frac{v(r+s)}{2}. \]
After simplifying with \(k=u+v\), this becomes
\[ us \ge (u+2v)r. \]
So for fixed \(u,v,r\), the first feasible value of \(s\) is
\[ s_{\min}=\left\lceil\frac{(u+2v)r}{u}\right\rceil. \]
Because \(s=s_0n^2\), the allowed range for \(n\) is
\[ n_{\min}=\left\lceil\sqrt{\left\lceil\frac{s_{\min}}{s_0}\right\rceil}\right\rceil, \qquad n_{\max}=\left\lfloor\sqrt{\frac{L}{s_0}}\right\rfloor. \]
The parity condition \(r \equiv s \pmod 2\) now becomes a filter on \(n\). If \(s_0\) is even, then every admissible \(s\) is even, so only even \(r\) can contribute. If \(s_0\) is odd, then \(s \equiv n^2 \equiv n \pmod 2\), so we need
\[ n \equiv r \pmod 2. \]
Each branch therefore contributes only the number of integers \(n\) in a short interval with the correct parity.
Why the outer loop stops at \(k \le \lfloor\sqrt[3]{2N^2}\rfloor+2\)
The solutions use a sharp global bound on \(k\). Since \(v \ge k/2\) and \(d \ge 1\), any valid branch must satisfy
\[ vd \le rs \le s^2 \le L^2 \le \left(\frac{N}{k}\right)^2. \]
Therefore
\[ \frac{k}{2} \le \left(\frac{N}{k}\right)^2, \qquad\text{so}\qquad k^3 \le 2N^2. \]
This is why only \(k\) up to roughly \((2N^2)^{1/3}\) need to be examined.
Worked example
Consider the valid triangle \((a,b,c)=(12,15,18)\). Then
\[ g=\gcd(12,15)=3,\qquad u=4,\qquad v=5,\qquad k=9. \]
Since \(c=18=9\cdot 2\), we have \(t=2\). Hence
\[ r=g-t=1,\qquad s=g+t=5. \]
Recovering the sides from \((u,v,r,s)\) gives
\[ a=\frac{4(1+5)}{2}=12,\qquad b=\frac{5(1+5)}{2}=15,\qquad c=\frac{9(5-1)}{2}=18. \]
The square test becomes
\[ Q=\frac{4^3 \cdot 1 \cdot 5}{5}=64=8^2. \]
Here \(\operatorname{sf}(4)=1\), so the square condition reduces to \(s/5\) being a square when \(r=1\). The smallest choice is \(s_0=5\), and \(s=5=s_0\cdot 1^2\) is indeed the first admissible value above
\[ s_{\min}=\left\lceil\frac{(4+2\cdot 5)\cdot 1}{4}\right\rceil=4. \]
This single example mirrors the whole algorithm: fix a reduced ratio \(u:v\), turn the triangle into \(r\) and \(s\), build the minimal base \(s_0\), then count square multiples inside the allowed interval.
How the Code Works
Prime tables and squarefree kernels
The C++, Python, and Java implementations begin by computing a smallest-prime-factor table. From it they derive two reusable data sets: the squarefree part \(\operatorname{sf}(u)\) for every possible \(u\), and a compact prime-exponent factorization for every integer that can appear as \(v\), \(r\), or \(\operatorname{sf}(u)\). This removes repeated factorization work from the main count.
Processing one reduced ratio
For each \(k\), the implementation iterates over \(v\) from \(\lceil k/2 \rceil\) to \(k-1\), keeps only \(\gcd(v,k)=1\), and sets \(u=k-v\). It then computes \(L=\lfloor N/k \rfloor\) and discards the branch immediately when \(v\operatorname{sf}(u) \gt L^2\), because then even the largest possible product \(rs\) cannot satisfy the square condition.
Next it loops over
\[ 1 \le r \le \left\lfloor\frac{uL}{u+2v}\right\rfloor. \]
For each such \(r\), the implementation merges the prime data of \(r\), \(v\), and \(\operatorname{sf}(u)\) to construct the smallest \(s_0\) that can make \(drs/v\) a square. If \(s_0 \gt L\), the branch is impossible. Otherwise the code converts the conditions into the interval \([n_{\min},n_{\max}]\), applies the parity rule, and adds the number of admissible \(n\).
Final accumulation
The total answer is the sum of these contributions over all \(k\). The Python implementation evaluates the outer \(k\)-loop serially. The C++ and Java implementations use parallel execution for that outer sweep, because each \(k\) contributes independently once the factor tables have been built.
Complexity Analysis
The outer bound \(k \le O(N^{2/3})\) already cuts the search space drastically. For a fixed \(k\), there are \(O(k)\) candidates for \(v\), and for each surviving \((u,v)\) the parameter \(r\) ranges up to \(O(N/k)\). A coarse upper bound for the arithmetic branches is therefore
\[ \sum_{k \le O(N^{2/3})} O(k)\,O\!\left(\frac{N}{k}\right)=O(N^{5/3}). \]
Each branch only performs a short merge of prime-exponent lists together with constant-time integer-root and interval calculations, so the practical runtime is much better than a triangle-by-triangle scan. The early coprimality filter and the \(v\operatorname{sf}(u) \le L^2\) pruning remove many branches before any expensive work happens.
Memory usage is dominated by the prime tables: the smallest-prime-factor sieve, the squarefree-part array, and the factor table up to the largest needed integer. This is essentially linear in the sieve bound, with only a small logarithmic factor from storing prime exponents.
Footnotes and References
- Problem page: https://projecteuler.net/problem=962
- Angle bisector theorem: Wikipedia - Angle bisector theorem
- Greatest common divisor: Wikipedia - Greatest common divisor
- Difference of two squares: Wikipedia - Difference of two squares
- Square-free integer: Wikipedia - Square-free integer
- Perfect square: Wikipedia - Perfect square
- Triangle inequality: Wikipedia - Triangle inequality