Problem 410: Circle and Tangent Line
View on Project EulerProject Euler Problem 410 Solution
EulerSolve provides an optimized solution for Project Euler Problem 410, Circle and Tangent Line, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary The geometric statement can be converted into an arithmetic counting problem. For a fixed bound pair \((A,B)\), every admissible tangent configuration is encoded by a positive integer \(n \le A\), a positive integer \(b \le B\), and a factor pair of the square \(b^2\). The direct formulation is still too slow, but it exposes enough structure to turn the geometry into a divisor sum. The official task requires two such counts, for \((10^8,10^9)\) and \((10^9,10^8)\), and adds them together. The whole challenge is therefore to derive a fast closed form for a single subproblem \(F(A,B)\). Mathematical Approach Step 1: Arithmetic form of the tangent condition The direct counting model shows that \(F(A,B)\) can be written in terms of positive integers satisfying $1 \le n \le A,\qquad 1 \le b \le B,\qquad cd=b^2,$ together with the two arithmetic constraints $b \mid n(c+d),\qquad \frac{n(c+d)}{b}\equiv d-c \pmod 2.$ Whenever these conditions hold, there are two symmetric sign choices, so each admissible arithmetic encoding contributes exactly \(2\) geometric configurations. This is the only part where the geometry still appears; from here onward the problem is pure number theory. Step 2: Every factor pair of a square has a coprime-square core Let \(\lambda=\gcd(c,d)\). Then \(c=\lambda c_0\), \(d=\lambda d_0\), and \(\gcd(c_0,d_0)=1\)....
Detailed mathematical approach
Problem Summary
The geometric statement can be converted into an arithmetic counting problem. For a fixed bound pair \((A,B)\), every admissible tangent configuration is encoded by a positive integer \(n \le A\), a positive integer \(b \le B\), and a factor pair of the square \(b^2\). The direct formulation is still too slow, but it exposes enough structure to turn the geometry into a divisor sum.
The official task requires two such counts, for \((10^8,10^9)\) and \((10^9,10^8)\), and adds them together. The whole challenge is therefore to derive a fast closed form for a single subproblem \(F(A,B)\).
Mathematical Approach
Step 1: Arithmetic form of the tangent condition
The direct counting model shows that \(F(A,B)\) can be written in terms of positive integers satisfying
$1 \le n \le A,\qquad 1 \le b \le B,\qquad cd=b^2,$
together with the two arithmetic constraints
$b \mid n(c+d),\qquad \frac{n(c+d)}{b}\equiv d-c \pmod 2.$
Whenever these conditions hold, there are two symmetric sign choices, so each admissible arithmetic encoding contributes exactly \(2\) geometric configurations. This is the only part where the geometry still appears; from here onward the problem is pure number theory.
Step 2: Every factor pair of a square has a coprime-square core
Let \(\lambda=\gcd(c,d)\). Then \(c=\lambda c_0\), \(d=\lambda d_0\), and \(\gcd(c_0,d_0)=1\). Since \(cd=b^2\), we obtain
$c_0d_0=\left(\frac{b}{\lambda}\right)^2.$
A product of two coprime positive integers can be a square only if each factor is already a square. Hence there exist coprime integers \(p,q \ge 1\) such that
$c=\lambda p^2,\qquad d=\lambda q^2,\qquad \gcd(p,q)=1,$
and therefore
$b=\lambda pq.$
This is the key normalization step: the square condition is no longer hidden inside divisors of \(b^2\); it is explicit in the parameters \((\lambda,p,q)\).
Step 3: The divisibility condition collapses to \(pq \mid n\)
Substituting the decomposition above into the divisibility condition gives
$\lambda pq \mid n\lambda(p^2+q^2),$
so equivalently
$pq \mid n(p^2+q^2).$
Now \(\gcd(p,q)=1\), and no prime dividing \(pq\) can divide \(p^2+q^2\). Therefore
$\gcd(pq,p^2+q^2)=1,$
which forces
$pq \mid n.$
Write
$m=pq,\qquad n=mt.$
Then \(t\) can range through
$1 \le t \le \left\lfloor\frac{A}{m}\right\rfloor,$
while the scale factor \(\lambda\) is constrained only by \(b=\lambda m \le B\), hence
$1 \le \lambda \le \left\lfloor\frac{B}{m}\right\rfloor.$
Step 4: Why the weight is \(2^{\omega(m)}\)
Fix \(m\). We must count ordered coprime pairs \((p,q)\) such that \(pq=m\). If
$m=\prod_{i=1}^{r}\ell_i^{e_i},$
then coprimality means each whole prime power \(\ell_i^{e_i}\) must go entirely to \(p\) or entirely to \(q\). Every distinct prime gives exactly two choices, so the number of ordered pairs is
$2^r=2^{\omega(m)},$
where \(\omega(m)\) is the number of distinct prime divisors of \(m\). This explains the multiplicative weight used in the implementation.
Step 5: Odd \(m\) and even \(m\) behave differently
After substitution, the parity condition becomes
$\frac{n(c+d)}{b}=t(p^2+q^2),\qquad d-c=\lambda(q^2-p^2).$
We need these two quantities to have the same parity. Since
$p^2+q^2\equiv q^2-p^2 \pmod 2,$
the condition is equivalent to
$ (t-\lambda)(p^2+q^2)\equiv 0 \pmod 2.$
If \(m\) is odd, then both \(p\) and \(q\) are odd, so \(p^2+q^2\) is even. The parity restriction disappears completely. For each of the \(2^{\omega(m)}\) coprime factorizations, every pair \((t,\lambda)\) is valid, and the two sign choices contribute
$2\left\lfloor\frac{A}{m}\right\rfloor\left\lfloor\frac{B}{m}\right\rfloor.$
If \(m\) is even, exactly one of \(p,q\) is even, so \(p^2+q^2\) is odd. Now we must have \(t\equiv\lambda\pmod 2\). Let
$t_{\max}=\left\lfloor\frac{A}{m}\right\rfloor,\qquad \lambda_{\max}=\left\lfloor\frac{B}{m}\right\rfloor.$
The number of pairs \((t,\lambda)\) with equal parity is
$\left\lceil\frac{t_{\max}}{2}\right\rceil\left\lceil\frac{\lambda_{\max}}{2}\right\rceil+\left\lfloor\frac{t_{\max}}{2}\right\rfloor\left\lfloor\frac{\lambda_{\max}}{2}\right\rfloor =\frac{t_{\max}\lambda_{\max}+(t_{\max}\bmod 2)(\lambda_{\max}\bmod 2)}{2}.$
After multiplying by the two sign choices, the even case contributes
$t_{\max}\lambda_{\max}+(t_{\max}\bmod 2)(\lambda_{\max}\bmod 2).$
Step 6: Final summation formula
Define the parity-dependent unit contribution by
$U_m(A,B)= \begin{cases} 2\left\lfloor\frac{A}{m}\right\rfloor\left\lfloor\frac{B}{m}\right\rfloor, & m\ \mathrm{odd},\\[6pt] \left\lfloor\frac{A}{m}\right\rfloor\left\lfloor\frac{B}{m}\right\rfloor+ \left(\left\lfloor\frac{A}{m}\right\rfloor\bmod 2\right)\left(\left\lfloor\frac{B}{m}\right\rfloor\bmod 2\right), & m\ \mathrm{even}. \end{cases}$
Then the whole subproblem is
$\boxed{F(A,B)=\sum_{m=1}^{\min(A,B)}2^{\omega(m)}\,U_m(A,B).}$
The required total is
$F(10^8,10^9)+F(10^9,10^8).$
The summand is symmetric in \(A\) and \(B\), so the two terms are equal, but writing the result as a sum of two evaluations mirrors the original statement and the implementations.
Worked Example: \(F(2,10)=52\)
Only \(m=1\) and \(m=2\) can contribute.
For \(m=1\), we have \(\omega(1)=0\), so the weight is \(1\). Since \(1\) is odd,
$U_1(2,10)=2\cdot 2\cdot 10=40.$
For \(m=2\), we have \(\omega(2)=1\), so the weight is \(2\). Since \(2\) is even,
$U_2(2,10)=\left\lfloor\frac{2}{2}\right\rfloor\left\lfloor\frac{10}{2}\right\rfloor+ \left(\left\lfloor\frac{2}{2}\right\rfloor\bmod 2\right)\left(\left\lfloor\frac{10}{2}\right\rfloor\bmod 2\right) =1\cdot 5+1\cdot 1=6.$
Therefore
$F(2,10)=1\cdot 40+2\cdot 6=52,$
which matches the checkpoint used by the implementation.
How the Code Works
The C++, Python, and Java implementations first precompute \(\omega(m)\) for every \(m\) up to the largest required limit by a sieve: each prime visits all of its multiples once, so every integer accumulates its number of distinct prime divisors. Then the implementation evaluates the summation above term by term, using only floor divisions, parity checks, a bit shift for \(2^{\omega(m)}\), and integer accumulation.
The same scalar routine is applied to the two official bound pairs. Because each \(m\)-term is independent, the outer sum is naturally parallelizable; the C++ and Java implementations exploit that independence, while the Python implementation keeps the same mathematics in a straightforward single-threaded loop.
Complexity Analysis
Let \(L=\max(\min(10^8,10^9),\min(10^9,10^8))=10^8\). Building the distinct-prime-factor table costs \(O(L\log\log L)\) time and \(O(L)\) memory. The final summation is \(O(L)\) time because every \(m\) contributes a constant-time term. Thus the overall method runs in \(O(L\log\log L)\) time and uses \(O(L)\) space.
References
- Problem page: https://projecteuler.net/problem=410
- Prime omega function: Wikipedia — Prime omega function
- Fundamental theorem of arithmetic: Wikipedia — Fundamental theorem of arithmetic
- Sieve of Eratosthenes: Wikipedia — Sieve of Eratosthenes
- Hardy, G. H., Wright, E. M. An Introduction to the Theory of Numbers. Sections on unique factorization and arithmetic functions.