Problem Summary

For a positive radius \(R\), let \(F(R)\) denote the largest possible inradius of a primitive integer right triangle that fits strictly inside a circle of radius \(R\). If the legs are \(a\) and \(b\), the hypotenuse is \(c\), and the inradius is \(r\), the challenge is to evaluate \(F(10^{18})\).

The geometric condition is much simpler than it first appears. A right triangle has circumradius \(c/2\), so fitting strictly inside the circle is equivalent to requiring \(c<2R\). The solution therefore becomes an optimization over primitive Pythagorean triples, and the implementations succeed by combining an exact formula for \(r\), a monotonicity argument for fixed parameters, and an exact continuous upper bound that collapses the search interval.

Mathematical Approach

From the circle constraint to Euclid's parameters

Every primitive Pythagorean triple can be written uniquely as

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

with integers \(m>n>0\), \(\gcd(m,n)=1\), and \(m\not\equiv n\pmod 2\). Since the triangle is right-angled, its smallest enclosing circle has radius \(c/2\), so the condition \(c<2R\) becomes

$$m^2+n^2<2R.$$

Because the inequality is strict, it is convenient to set

$$T=2R-1,$$

so that admissible pairs are exactly those with

$$m^2+n^2\le T.$$

For a right triangle the inradius is

$$r=\frac{a+b-c}{2},$$

and substituting Euclid's formulas gives the remarkably simple objective

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

So the entire problem becomes

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

Why each \(n\) has a single best partner

Fix \(n\). Then the score \(n(m-n)\) increases strictly with \(m\), so only the largest admissible \(m\) can be optimal for that \(n\). Ignoring parity and coprimality for a moment, the geometric ceiling is

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

If \(m_{\max}(n)\le n\), then no triangle exists for that \(n\). Otherwise the primitive-triple conditions are enforced in the only direction that matters: start from \(m_{\max}(n)\), decrease once if \(m\) has the same parity as \(n\), and then keep decreasing by \(2\) until \(\gcd(m,n)=1\). The first pair that survives is already the best one for that \(n\), because every later candidate has a smaller \(m\) and therefore a smaller inradius.

This also yields the natural outer range for the search. Since every feasible pair has \(m>n\), we must have

$$2n^2<m^2+n^2\le T,$$

hence

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

The continuous envelope that makes pruning exact

Now relax the arithmetic conditions and allow \(m\) to move on the real boundary \(m=\sqrt{T-n^2}\). Then every integer candidate with the same \(n\) satisfies

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

This function is a true upper bound, not a heuristic estimate. Differentiating \(U(n)\) shows that its unique peak occurs when the boundary value of \(m\) and the chosen \(n\) satisfy

$$m=(1+\sqrt 2)\,n.$$

Equivalently, the maximizing real \(n\) satisfies

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

So the best triangle must lie near one continuous optimum rather than somewhere arbitrary in the whole interval. Once a current record is known, every \(n\) with \(U(n)\) no larger than that record can be discarded immediately. Because \(U(n)\) rises to a single peak and then falls, the surviving region is one interval around \(n_0\), which can be found accurately by bisection on the left and right sides.

Worked Example: \(R=100\)

Here \(T=199\), so admissibility means

$$m^2+n^2\le 199.$$

The continuous peak is near

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

which already tells us that the optimum should occur close to \(n=5\). For \(n=4\),

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

The pair \((13,4)\) already has opposite parity and \(\gcd(13,4)=1\), so it produces

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

and therefore

$$r=4(13-4)=36.$$

Since \(c/2=92.5<100\), the triangle really does fit inside the circle. Nearby candidates are slightly worse: \(n=5\) leads to \(m=12\) and \(r=35\), while \(n=6\) leads to \(m=11\) and \(r=30\). Thus \(F(100)=36\).

How the Code Works

Shared search strategy

The C++, Python, and Java implementations all follow the same logic. They compute exact integer square roots so that the strict boundary \(m^2+n^2<2R\) is handled as \(m^2+n^2\le 2R-1\) without any floating-point off-by-one errors. They then estimate the location of the continuous peak with \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\), scan a wide window around that point to obtain a strong initial record, and evaluate each tested \(n\) by starting from \(\lfloor\sqrt{2R-1-n^2}\rfloor\), fixing parity if necessary, and descending by \(2\) until a coprime partner is found.

Exact pruning and final sweep

After the initial window, the implementations use the upper envelope \(U(n)=n(\sqrt{2R-1-n^2}-n)\) to determine which \(n\)-values can still beat the current best answer. Because \(U\) is unimodal, bisection on each side of the peak isolates the real interval where improvement is still possible. That interval is then converted back to integers and widened slightly to stay safe against rounding.

Inside the final sweep, the code applies the sharper integer bound

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

before spending time on gcd tests. If even this value cannot improve the current record, that \(n\) is skipped immediately. The pruning is therefore exact: every discarded case has been eliminated by a proved upper bound.

Complexity Analysis

The natural search variable is \(n\), and its full admissible range has size \(O(\sqrt R)\). For each tested \(n\), the implementation performs one integer square root, possibly one parity correction, and then a downward walk through values of the correct parity until it reaches a coprime partner. So the basic structure is an \(O(\sqrt R)\)-sized search with very small per-candidate work in practice.

The continuous bound \(U(n)\) makes the real running time much smaller than a full scan, because once a good record is found only a narrow band around the peak can remain relevant. Memory usage is \(O(1)\), since the algorithm stores only a small number of integer and floating-point variables.

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=914
  2. Pythagorean triple: Wikipedia - Pythagorean triple
  3. Incircle and excircles of a triangle: Wikipedia - Incircle and excircles of a triangle
  4. Thales's theorem: Wikipedia - Thales's theorem
  5. Greatest common divisor: Wikipedia - Greatest common divisor
  6. Integer square root: Wikipedia - Integer square root

Problemzusammenfassung

Für einen positiven Radius \(R\) sei \(F(R)\) der größtmögliche Inkreisradius eines primitiven rechtwinkligen Dreiecks mit ganzzahligen Seiten, das strikt in einen Kreis mit Radius \(R\) passt. Sind \(a\) und \(b\) die Katheten, \(c\) die Hypotenuse und \(r\) der Inkreisradius, so besteht die Aufgabe darin, \(F(10^{18})\) zu bestimmen.

Die Geometrie vereinfacht die Bedingung sofort. Ein rechtwinkliges Dreieck hat den Umkreisradius \(c/2\), also ist „passt strikt in den Kreis“ genau äquivalent zu \(c<2R\). Damit wird die Aufgabe zu einer Optimierung über primitive pythagoreische Tripel. Die Implementierungen lösen sie durch eine exakte Formel für \(r\), ein Monotonieargument für festes \(n\) und eine scharfe stetige obere Schranke, die den Suchbereich drastisch verkleinert.

Mathematischer Ansatz

Von der Kreisbedingung zur euklidischen Parametrisierung

Jedes primitive pythagoreische Tripel lässt sich eindeutig schreiben als

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

wobei \(m>n>0\), \(\gcd(m,n)=1\) und \(m\not\equiv n\pmod 2\) gelten. Da das Dreieck rechtwinklig ist, hat sein kleinster umschließender Kreis den Radius \(c/2\). Die Bedingung \(c<2R\) wird daher zu

$$m^2+n^2<2R.$$

Weil die Ungleichung strikt ist, setzt man bequem

$$T=2R-1,$$

so dass die zulässigen Paare genau durch

$$m^2+n^2\le T$$

beschrieben werden. Für ein rechtwinkliges Dreieck gilt außerdem

$$r=\frac{a+b-c}{2},$$

und nach Einsetzen der Euklid-Formeln vereinfacht sich das zu

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

Damit lautet die gesamte Aufgabe

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

Warum jedes \(n\) genau einen besten Partner hat

Ist \(n\) fest, so wächst der Wert \(n(m-n)\) streng mit \(m\). Also kann für dieses \(n\) nur das größte zulässige \(m\) optimal sein. Lässt man Parität und Teilerfremdheit zunächst weg, so ist die geometrische Obergrenze

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

Falls \(m_{\max}(n)\le n\), gibt es für dieses \(n\) überhaupt kein zulässiges Dreieck. Andernfalls werden die Bedingungen für primitive Tripel genau in der einzig sinnvollen Richtung erzwungen: Man startet bei \(m_{\max}(n)\), reduziert einmal, falls \(m\) dieselbe Parität wie \(n\) hat, und geht dann in Zweierschritten weiter nach unten, bis \(\gcd(m,n)=1\) gilt. Das erste erfolgreiche Paar ist bereits optimal, weil jedes spätere Paar ein kleineres \(m\) und damit einen kleineren Inkreisradius besitzt.

Daraus folgt auch die natürliche äußere Suchgrenze. Aus \(m>n\) erhält man

$$2n^2<m^2+n^2\le T,$$

also

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

Die stetige Hülle, die exaktes Aussieben erlaubt

Nun lockern wir die arithmetischen Bedingungen und lassen zu, dass \(m\) den reellen Randwert \(m=\sqrt{T-n^2}\) annimmt. Dann erfüllt jeder ganzzahlige Kandidat mit demselben \(n\) die Schranke

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

Diese Funktion ist keine Heuristik, sondern eine echte obere Schranke. Durch Ableiten von \(U(n)\) sieht man, dass ihr eindeutiges Maximum genau dann erreicht wird, wenn

$$m=(1+\sqrt 2)\,n$$

gilt. Äquivalent dazu erfüllt der optimale reelle Wert von \(n\)

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

Das beste Dreieck liegt also in der Nähe eines einzigen stetigen Maximums und nicht irgendwo verteilt im gesamten Bereich. Sobald ein aktueller Rekord bekannt ist, kann jedes \(n\) mit \(U(n)\) kleiner oder gleich diesem Rekord sofort verworfen werden. Weil \(U(n)\) zu einem einzigen Gipfel ansteigt und danach wieder fällt, bildet der verbleibende Bereich genau ein Intervall um \(n_0\), das sich links und rechts per Bisektion bestimmen lässt.

Durchgerechnetes Beispiel: \(R=100\)

Hier ist \(T=199\), also lautet die Zulässigkeitsbedingung

$$m^2+n^2\le 199.$$

Das stetige Maximum liegt bei

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

was bereits zeigt, dass die optimale ganzzahlige Lösung nahe bei \(n=5\) liegen sollte. Für \(n=4\) ist

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

Das Paar \((13,4)\) hat bereits verschiedene Parität und \(\gcd(13,4)=1\), also erhält man

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

und damit

$$r=4(13-4)=36.$$

Da \(c/2=92.5<100\) gilt, passt das Dreieck tatsächlich in den Kreis. Nahe Kandidaten sind etwas schlechter: \(n=5\) führt zu \(m=12\) und \(r=35\), \(n=6\) zu \(m=11\) und \(r=30\). Also ist \(F(100)=36\).

Wie der Code funktioniert

Gemeinsame Suchstrategie

Die Implementierungen in C++, Python und Java folgen demselben Ablauf. Zuerst berechnen sie exakte ganzzahlige Quadratwurzeln, damit die strikte Grenze \(m^2+n^2<2R\) als \(m^2+n^2\le 2R-1\) ohne Rundungsfehler behandelt werden kann. Anschließend schätzen sie die Lage des stetigen Maximums mit \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\), durchsuchen ein breites Fenster um diesen Punkt, um schnell einen starken Anfangsrekord zu bekommen, und werten jedes getestete \(n\) aus, indem sie bei \(\lfloor\sqrt{2R-1-n^2}\rfloor\) starten, gegebenenfalls die Parität korrigieren und dann in Zweierschritten abwärts gehen, bis ein teilerfremdes Paar gefunden ist.

Exaktes Aussieben und finaler Durchlauf

Nach diesem ersten Fenster benutzen die Implementierungen die obere Hülle \(U(n)=n(\sqrt{2R-1-n^2}-n)\), um festzustellen, welche \(n\)-Werte den aktuellen Bestwert überhaupt noch schlagen können. Weil \(U\) unimodal ist, isoliert eine Bisektion auf beiden Seiten des Gipfels genau das reelle Intervall, in dem noch Verbesserungen möglich sind. Dieses Intervall wird anschließend zurück in ganze Zahlen übersetzt und leicht vergrößert, damit keine Rundungseffekte verloren gehen.

Innerhalb des letzten Durchlaufs verwendet der Code zusätzlich die schärfere ganzzahlige Schranke

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

bevor überhaupt ggT-Tests ausgeführt werden. Wenn selbst diese Zahl den aktuellen Rekord nicht verbessern kann, wird das entsprechende \(n\) sofort übersprungen. Das Aussieben ist also exakt: Jeder verworfene Fall wurde durch eine bewiesene obere Schranke ausgeschlossen.

Komplexitätsanalyse

Die natürliche Suchvariable ist \(n\), und ihr vollständiger zulässiger Bereich hat Größe \(O(\sqrt R)\). Für jedes getestete \(n\) braucht die Implementierung eine ganzzahlige Quadratwurzel, eventuell eine Paritätskorrektur und danach einen Abstieg über Werte mit passender Parität, bis ein teilerfremder Partner erreicht ist. Strukturell handelt es sich also um eine Suche der Größe \(O(\sqrt R)\) mit in der Praxis sehr kleinem Aufwand pro Kandidat.

Die stetige Schranke \(U(n)\) reduziert die tatsächliche Laufzeit stark, weil nach einem guten Anfangsrekord nur noch ein schmales Band um das Maximum relevant bleibt. Der Speicherbedarf ist \(O(1)\), denn es werden nur wenige ganzzahlige und Gleitkommawerte gehalten.

Fußnoten und Referenzen

  1. Problemseite: https://projecteuler.net/problem=914
  2. Pythagoreisches Tripel: Wikipedia - Pythagoreisches Tripel
  3. Inkreis und Ankreise: Wikipedia - Inkreis
  4. Satz des Thales: Wikipedia - Satz des Thales
  5. Größter gemeinsamer Teiler: Wikipedia - Größter gemeinsamer Teiler
  6. Ganzzahlige Quadratwurzel: Wikipedia - Integer square root

Problem Özeti

Pozitif bir \(R\) yarıçapı için \(F(R)\), yarıçapı \(R\) olan bir çemberin içine sıkı biçimde sığan ilkel tamsayılı dik üçgenler arasındaki en büyük içyarıçap olsun. Dik kenarlar \(a\) ve \(b\), hipotenüs \(c\), içyarıçap \(r\) ise amaç \(F(10^{18})\) değerini hesaplamaktır.

Geometrik koşul ilk bakışta göründüğünden daha basittir. Bir dik üçgenin çevrel çember yarıçapı \(c/2\) olduğundan, çemberin içine sıkı biçimde sığmak tam olarak \(c<2R\) demektir. Böylece problem ilkel Pisagor üçlüleri üzerinde bir optimizasyona dönüşür. Çözüm de \(r\) için kapalı formülü, sabit \(n\) için monotonluğu ve arama aralığını daraltan kesin bir sürekli üst sınırı birleştirir.

Matematiksel Yaklaşım

Çember koşulundan Euclid parametrelerine

Her ilkel Pisagor üçlüsü tekil olarak

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

şeklinde yazılır; burada \(m>n>0\), \(\gcd(m,n)=1\) ve \(m\not\equiv n\pmod 2\) gerekir. Üçgen dik olduğu için onu çevreleyen en küçük çemberin yarıçapı \(c/2\)'dir. Dolayısıyla \(c<2R\) koşulu

$$m^2+n^2<2R$$

eşitsizliğine dönüşür. Eşitsizlik sıkı olduğu için

$$T=2R-1$$

tanımını yapmak uygundur; böylece geçerli çiftler tam olarak

$$m^2+n^2\le T$$

koşulunu sağlar. Dik üçgende içyarıçap

$$r=\frac{a+b-c}{2}$$

olduğundan, Euclid formülleri yerine yazılınca hedef fonksiyon

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n)$$

haline gelir. Böylece tüm problem

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}$$

biçiminde yazılır.

Neden her \(n\) için tek bir en iyi eş vardır

\(n\) sabitken \(n(m-n)\) ifadesi \(m\) arttıkça sıkı biçimde büyür. Bu yüzden o \(n\) için yalnızca en büyük geçerli \(m\) önemli olabilir. Parite ve aralarında asal olma koşullarını geçici olarak yok sayarsak geometrik üst sınır

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor$$

olur. Eğer \(m_{\max}(n)\le n\) ise o \(n\) için hiç üçgen yoktur. Aksi halde ilkel üçlü koşulları tek anlamlı yönde uygulanır: \(m_{\max}(n)\)'den başlanır, \(m\) ile \(n\) aynı paritedeyse bir kez azaltılır, sonra \(\gcd(m,n)=1\) olana kadar \(2\) adımlarla aşağı inilir. Bu testi geçen ilk çift, o \(n\) için zaten en iyi çifttir; çünkü daha sonra gelen her aday daha küçük bir \(m\) ve dolayısıyla daha küçük bir içyarıçap verir.

Bu gözlem aramanın doğal dış sınırını da verir. Her geçerli çift için \(m>n\) olduğundan

$$2n^2<m^2+n^2\le T$$

ve dolayısıyla

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor$$

olmalıdır.

Kesin budamayı mümkün kılan sürekli zarf

Şimdi aritmetik koşulları gevşetip \(m\)'nin gerçek sayı olarak sınır değeri \(m=\sqrt{T-n^2}\) olmasına izin verelim. O zaman aynı \(n\) için her tamsayı aday

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right)$$

üst sınırını sağlar. Bu fonksiyon sezgisel bir puan değil, gerçek bir üst sınırdır. \(U(n)\)'nin türevi alındığında tek tepe noktasının

$$m=(1+\sqrt 2)\,n$$

ilişkisi altında oluştuğu görülür. Eşdeğer olarak, en iyi gerçek \(n\) değeri

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}$$

eşitliğini sağlar. Yani en iyi üçgen, tüm aralığa dağılmış bir noktada değil, tek bir sürekli optimumun çevresinde aranmalıdır. Geçerli bir mevcut rekor bulunduğunda \(U(n)\) değeri bu rekoru bile aşamayan her \(n\) anında elenebilir. Çünkü \(U(n)\) tek bir tepeye yükselip sonra azaldığı için, geriye kalan adaylar \(n_0\) çevresindeki tek bir aralık oluşturur; bu aralık da sağ ve solda ikili arama ile hassas biçimde bulunabilir.

Çalışılmış Örnek: \(R=100\)

Burada \(T=199\) olur; yani geçerlilik koşulu

$$m^2+n^2\le 199$$

şeklindedir. Sürekli tepe noktası

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40$$

olduğundan, en iyi tamsayı çözümün \(n=5\) civarında olması beklenir. \(n=4\) için

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13$$

elde edilir. \((13,4)\) çifti zaten zıt pariteli ve aralarında asaldır; dolayısıyla

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185$$

ve buradan

$$r=4(13-4)=36$$

çıkar. Ayrıca \(c/2=92.5<100\) olduğu için üçgen gerçekten çemberin içine sığar. Yakın adaylar biraz daha kötüdür: \(n=5\) için \(m=12\) ve \(r=35\), \(n=6\) için \(m=11\) ve \(r=30\) olur. Demek ki \(F(100)=36\).

Kod Nasıl Çalışır

Ortak arama stratejisi

C++, Python ve Java uygulamaları aynı mantığı izler. Önce tam sayı karekökünü kesin olarak hesaplayarak \(m^2+n^2<2R\) sınırını \(m^2+n^2\le 2R-1\) biçiminde kayan nokta hatası olmadan uygularlar. Ardından sürekli tepe noktasını \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\) ile tahmin eder, bu noktanın çevresinde geniş bir pencere tarayarak güçlü bir başlangıç rekoru elde eder ve denenen her \(n\) için \(\lfloor\sqrt{2R-1-n^2}\rfloor\)'den başlayıp gerekirse pariteyi düzelterek, sonra da \(2\) adımlarla aşağı inip aralarında asal ilk eşi bularak değeri hesaplarlar.

Kesin budama ve son tarama

İlk pencere taramasından sonra uygulamalar \(U(n)=n(\sqrt{2R-1-n^2}-n)\) üst zarfını kullanarak hangi \(n\) değerlerinin mevcut en iyi sonucu hâlâ geçebileceğini belirler. \(U\) tek tepeli olduğu için, tepenin iki yanında yapılan ikili arama iyileştirme ihtimali kalan gerçek aralığı tam olarak izole eder. Bu aralık daha sonra tamsayılara çevrilir ve yuvarlama kaynaklı riskleri önlemek için biraz genişletilir.

Son tarama içinde kod, gcd hesaplarına geçmeden önce daha keskin olan

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

üst sınırını uygular. Bu değer bile mevcut rekoru geçemiyorsa o \(n\) doğrudan atlanır. Dolayısıyla budama tamamen kesindir: elenen her durum ispatlı bir üst sınırla dışarıda bırakılmıştır.

Karmaşıklık Analizi

Doğal arama değişkeni \(n\)'dir ve tüm geçerli aralığın büyüklüğü \(O(\sqrt R)\) seviyesindedir. Denenen her \(n\) için bir tam sayı karekökü, olası bir parite düzeltmesi ve ardından uygun paritedeki değerler üzerinde aralarında asal bir eş bulunana kadar aşağı yönlü kısa bir yürüyüş yapılır. Dolayısıyla temel yapı, pratikte aday başına çok küçük ek maliyetli bir \(O(\sqrt R)\) aramasıdır.

Sürekli üst sınır \(U(n)\), iyi bir rekor bulunduktan sonra yalnızca tepe çevresindeki dar bir bant açık kaldığı için gerçek çalışma süresini tam taramadan çok daha aşağı çeker. Bellek kullanımı \(O(1)\)'dir; yalnızca az sayıda tamsayı ve kayan nokta değişkeni tutulur.

Dipnotlar ve Referanslar

  1. Problem sayfası: https://projecteuler.net/problem=914
  2. Pisagor üçlüsü: Wikipedia - Pisagor üçlüsü
  3. İçteğet çember ve içyarıçap: Wikipedia - İçteğet çember
  4. Thales teoremi: Wikipedia - Thales teoremi
  5. EBOB: Wikipedia - En büyük ortak bölen
  6. Tam sayı karekökü: Wikipedia - Integer square root

Resumen del Problema

Para un radio positivo \(R\), sea \(F(R)\) el mayor inradio posible de un triángulo rectángulo primitivo con lados enteros que cabe estrictamente dentro de un círculo de radio \(R\). Si los catetos son \(a\) y \(b\), la hipotenusa es \(c\) y el inradio es \(r\), la meta es calcular \(F(10^{18})\).

La condición geométrica es más simple de lo que parece. Un triángulo rectángulo tiene circunradio \(c/2\), de modo que caber estrictamente en el círculo equivale exactamente a exigir \(c<2R\). Así, el problema se transforma en una optimización sobre ternas pitagóricas primitivas. Las implementaciones lo resuelven combinando una fórmula exacta para \(r\), un argumento de monotonía con \(n\) fijo y una cota superior continua y exacta que reduce de forma drástica la zona de búsqueda.

Enfoque Matemático

De la restricción del círculo a los parámetros de Euclides

Toda terna pitagórica primitiva puede escribirse de forma única como

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

con \(m>n>0\), \(\gcd(m,n)=1\) y \(m\not\equiv n\pmod 2\). Como el triángulo es rectángulo, su círculo mínimo envolvente tiene radio \(c/2\), así que la condición \(c<2R\) se convierte en

$$m^2+n^2<2R.$$

Como la desigualdad es estricta, resulta cómodo definir

$$T=2R-1,$$

de modo que los pares admisibles son exactamente los que satisfacen

$$m^2+n^2\le T.$$

Además, en un triángulo rectángulo el inradio vale

$$r=\frac{a+b-c}{2},$$

y al sustituir las fórmulas de Euclides se obtiene el objetivo

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

Por tanto, todo el problema queda reducido a

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

Por qué cada \(n\) tiene un único mejor compañero

Si fijamos \(n\), la cantidad \(n(m-n)\) crece estrictamente con \(m\). Por eso, para ese \(n\) solo puede ser óptimo el mayor \(m\) admisible. Si ignoramos por un momento la paridad y la coprimalidad, el techo geométrico es

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

Si \(m_{\max}(n)\le n\), entonces no existe ningún triángulo para ese \(n\). En caso contrario, las condiciones de primitividad se imponen en la única dirección relevante: se empieza en \(m_{\max}(n)\), se baja una vez si \(m\) tiene la misma paridad que \(n\), y después se sigue descendiendo de \(2\) en \(2\) hasta que \(\gcd(m,n)=1\). El primer par que supera estas pruebas ya es el mejor para ese \(n\), porque cualquier candidato posterior tiene un \(m\) menor y por tanto un inradio menor.

Esto también proporciona el límite exterior natural de la búsqueda. Como todo par factible satisface \(m>n\), se tiene

$$2n^2<m^2+n^2\le T,$$

y por lo tanto

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

La envolvente continua que hace exacta la poda

Ahora relajamos las condiciones aritméticas y permitimos que \(m\) tome el valor real del borde \(m=\sqrt{T-n^2}\). Entonces cualquier candidato entero con ese mismo \(n\) satisface

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

Esta función es una cota superior real, no una mera heurística. Al derivar \(U(n)\) se ve que su único máximo aparece cuando

$$m=(1+\sqrt 2)\,n.$$

Equivalentemente, el mejor \(n\) continuo satisface

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

Así, el mejor triángulo tiene que estar cerca de un único óptimo continuo, no en cualquier punto arbitrario del intervalo completo. Una vez que ya se conoce un buen récord, todo \(n\) con \(U(n)\) menor o igual que ese récord puede descartarse de inmediato. Como \(U(n)\) sube hasta un solo pico y luego baja, la región superviviente es un único intervalo alrededor de \(n_0\), y ese intervalo puede localizarse con precisión por bisección a izquierda y derecha.

Ejemplo trabajado: \(R=100\)

Aquí \(T=199\), de modo que la admisibilidad exige

$$m^2+n^2\le 199.$$

El pico continuo está cerca de

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

lo cual ya sugiere que la solución óptima entera aparecerá cerca de \(n=5\). Para \(n=4\),

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

El par \((13,4)\) ya tiene paridad opuesta y \(\gcd(13,4)=1\), así que produce

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

y por tanto

$$r=4(13-4)=36.$$

Como \(c/2=92.5<100\), el triángulo realmente cabe dentro del círculo. Los candidatos cercanos son algo peores: \(n=5\) lleva a \(m=12\) y \(r=35\), mientras que \(n=6\) lleva a \(m=11\) y \(r=30\). Por ello, \(F(100)=36\).

Cómo Funciona el Código

Estrategia de búsqueda compartida

Las implementaciones en C++, Python y Java siguen exactamente la misma idea. Primero calculan raíces cuadradas enteras exactas, para tratar la frontera estricta \(m^2+n^2<2R\) como \(m^2+n^2\le 2R-1\) sin errores de redondeo. Después estiman la posición del pico continuo mediante \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\), recorren una ventana amplia alrededor de ese punto para obtener rápidamente un buen récord inicial y evalúan cada \(n\) probado empezando desde \(\lfloor\sqrt{2R-1-n^2}\rfloor\), corrigiendo la paridad si hace falta y descendiendo de \(2\) en \(2\) hasta encontrar un compañero coprimo.

Poda exacta y barrido final

Tras esa ventana inicial, las implementaciones usan la envolvente \(U(n)=n(\sqrt{2R-1-n^2}-n)\) para decidir qué valores de \(n\) todavía pueden superar la mejor respuesta actual. Como \(U\) es unimodal, una bisección a cada lado del pico aísla el intervalo real donde aún puede haber mejora. Ese intervalo se convierte luego de nuevo en enteros y se amplía ligeramente para no perder ningún caso por redondeo.

Dentro del barrido final, el código aplica además la cota entera más afilada

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

antes de invertir tiempo en cálculos de gcd. Si ni siquiera ese valor puede mejorar el récord vigente, ese \(n\) se omite al instante. Por tanto, la poda es exacta: cada caso descartado ha sido eliminado por una cota superior demostrada.

Análisis de Complejidad

La variable natural de búsqueda es \(n\), y todo su rango admisible tiene tamaño \(O(\sqrt R)\). Para cada \(n\) probado, la implementación hace una raíz cuadrada entera, quizá una corrección de paridad y luego un descenso sobre valores de la paridad correcta hasta alcanzar un compañero coprimo. En consecuencia, la estructura básica es una búsqueda de tamaño \(O(\sqrt R)\) con un coste por candidato muy pequeño en la práctica.

La cota continua \(U(n)\) reduce mucho el tiempo real frente a un recorrido completo, porque después de hallar un buen récord solo sigue vivo un intervalo estrecho alrededor del pico. El consumo de memoria es \(O(1)\), ya que el algoritmo solo guarda unas pocas variables enteras y de punto flotante.

Notas y Referencias

  1. Página del problema: https://projecteuler.net/problem=914
  2. Terna pitagórica: Wikipedia - Terna pitagórica
  3. Inradio y círculo inscrito: Wikipedia - Círculo inscrito
  4. Teorema de Tales: Wikipedia - Teorema de Tales
  5. Máximo común divisor: Wikipedia - Máximo común divisor
  6. Raíz cuadrada entera: Wikipedia - Integer square root

问题概述

对一个正半径 \(R\),记 \(F(R)\) 为所有能够严格放进半径为 \(R\) 的圆内的原始整边直角三角形中,内切圆半径的最大值。若两条直角边为 \(a,b\),斜边为 \(c\),内切圆半径为 \(r\),那么题目的目标就是求出 \(F(10^{18})\)。

几何约束实际上比表面上简单得多。直角三角形的外接圆半径正好是 \(c/2\),所以“三角形严格位于半径为 \(R\) 的圆内”与 \(c<2R\) 完全等价。于是问题转化为对原始勾股三角形的优化。三份实现之所以能处理极大的 \(R\),关键在于三点:\(r\) 有精确闭式、固定 \(n\) 时目标函数对 \(m\) 单调递增、以及一个能够精确裁剪搜索区间的连续上包络。

数学方法

从圆的约束到欧几里得参数

每个原始勾股三角形都可以唯一表示为

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

其中 \(m>n>0\),\(\gcd(m,n)=1\),并且 \(m\not\equiv n\pmod 2\)。由于三角形是直角三角形,它的最小外包圆半径就是 \(c/2\),因此条件 \(c<2R\) 变成

$$m^2+n^2<2R.$$

因为这里是不等号严格小于,所以引入

$$T=2R-1,$$

就可以把可行条件改写为

$$m^2+n^2\le T.$$

另一方面,直角三角形的内切圆半径满足

$$r=\frac{a+b-c}{2},$$

代入欧几里得参数化后立刻化简为

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

于是整个题目被精确地改写成

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

为什么每个 \(n\) 只需要一个最优搭档

固定 \(n\) 之后,目标值 \(n(m-n)\) 随 \(m\) 严格递增。因此,对这个 \(n\) 来说,只有最大的可行 \(m\) 才可能最优。如果暂时忽略奇偶性和互素性,那么几何上界就是

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

若 \(m_{\max}(n)\le n\),则这个 \(n\) 根本不产生可行三角形。否则就按唯一有意义的方向修正:从 \(m_{\max}(n)\) 开始,如果它与 \(n\) 同奇同偶,就先减去 \(1\);然后再以步长 \(2\) 向下搜索,直到满足 \(\gcd(m,n)=1\)。第一个通过这些条件的 \(m\) 已经是该 \(n\) 下的最优选择,因为再往下的任何候选都会使 \(m\) 更小,从而使内切圆半径更小。

这也给出了搜索的自然外层范围。由于所有可行对都满足 \(m>n\),必有

$$2n^2<m^2+n^2\le T,$$

因此

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

使剪枝保持精确的连续上包络

接下来把数论条件暂时放宽,允许 \(m\) 取到边界上的实数值 \(m=\sqrt{T-n^2}\)。这样一来,所有具有相同 \(n\) 的整数候选都会满足

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

这里的 \(U(n)\) 不是启发式评分,而是严格成立的上界。对 \(U(n)\) 求导后可以看出,它只有一个峰值,而且峰值处满足

$$m=(1+\sqrt 2)\,n.$$

等价地,连续最优点的 \(n\) 满足

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

这说明最优三角形一定出现在某个单一连续极值附近,而不会散落在整个区间的任意位置。一旦当前最好答案已经找到,凡是满足 \(U(n)\) 不可能超过该记录的 \(n\),都可以立刻删除。又因为 \(U(n)\) 先升后降、只存在一个峰值,所以剩余可行区域必然是围绕 \(n_0\) 的一个区间,这个区间可以在峰值左右分别用二分法精确定位。

算例:\(R=100\)

此时 \(T=199\),可行条件为

$$m^2+n^2\le 199.$$

连续峰值位于

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

这已经提示最优整数解会落在 \(n=5\) 附近。对 \(n=4\),有

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

\((13,4)\) 本身就满足奇偶性相反,且 \(\gcd(13,4)=1\),因此得到

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

从而

$$r=4(13-4)=36.$$

由于 \(c/2=92.5<100\),这个三角形确实能够放进半径为 \(100\) 的圆内。附近候选略差一些:\(n=5\) 时得到 \(m=12\)、\(r=35\);\(n=6\) 时得到 \(m=11\)、\(r=30\)。因此 \(F(100)=36\)。

代码如何工作

统一的搜索策略

C++、Python 和 Java 三份实现采用同一条逻辑链。首先,它们精确计算整数平方根,从而把严格边界 \(m^2+n^2<2R\) 安全地改写为 \(m^2+n^2\le 2R-1\),不会出现浮点舍入导致的越界或漏判。然后,代码用 \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\) 估计连续峰值的位置,在该点附近扫一段较宽的窗口,快速得到一个足够强的当前最优值。对每个被测试的 \(n\),程序都从 \(\lfloor\sqrt{2R-1-n^2}\rfloor\) 出发,必要时先修正奇偶性,再以步长 \(2\) 向下寻找第一个互素搭档。

精确剪枝与最终扫描

完成初始窗口扫描后,程序用上包络 \(U(n)=n(\sqrt{2R-1-n^2}-n)\) 来判断哪些 \(n\) 仍然可能超过当前最优答案。由于 \(U\) 是单峰函数,在峰值两侧各做一次二分,就能把“仍有改进可能”的实数区间准确找出来。随后再把这个区间转回整数,并略微放宽一点,以吸收舍入误差。

进入最终扫描后,代码还会先应用更尖锐的整数上界

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

再决定是否需要做 gcd 检查。如果连这个上界都不可能打破当前记录,那么该 \(n\) 就会被直接跳过。因此这里的剪枝是完全精确的:每个被丢弃的情况都已经被严格的上界排除。

复杂度分析

自然的搜索变量是 \(n\),其完整可行范围大小为 \(O(\sqrt R)\)。对每个被检查的 \(n\),实现都要做一次整数平方根,可能再做一次奇偶性修正,然后沿着正确奇偶性的 \(m\) 值向下走,直到找到互素搭档。因此,从结构上看,这是一个规模为 \(O(\sqrt R)\) 的搜索,而每个候选在实践中的额外代价很小。

连续上界 \(U(n)\) 使真实运行时间远小于完整扫描,因为一旦拿到足够好的当前最优值,真正还需要检查的就只剩连续峰值附近的一条很窄的带。空间复杂度为 \(O(1)\),因为算法只保存少量整数和浮点边界变量。

注释与参考资料

  1. 题目页面: https://projecteuler.net/problem=914
  2. 勾股三元组: Wikipedia - 勾股数
  3. 内切圆与内切圆半径: Wikipedia - 内切圆
  4. 泰勒斯定理: Wikipedia - 泰勒斯定理
  5. 最大公因数: Wikipedia - 最大公因数
  6. 整数平方根: Wikipedia - Integer square root

Краткое описание

Для положительного радиуса \(R\) обозначим через \(F(R)\) наибольший возможный радиус вписанной окружности среди всех примитивных прямоугольных треугольников с целыми сторонами, которые строго помещаются внутри круга радиуса \(R\). Если катеты равны \(a\) и \(b\), гипотенуза равна \(c\), а радиус вписанной окружности равен \(r\), то требуется вычислить \(F(10^{18})\).

Геометрическое условие здесь гораздо проще, чем кажется сначала. У прямоугольного треугольника радиус описанной окружности равен \(c/2\), поэтому условие строгого размещения внутри круга в точности эквивалентно неравенству \(c<2R\). Значит, задача сводится к оптимизации по примитивным пифагоровым тройкам. Реализации решают ее, сочетая точную формулу для \(r\), монотонность при фиксированном \(n\) и точную непрерывную верхнюю оценку, резко сужающую область поиска.

Математический подход

От ограничения круга к параметрам Евклида

Любая примитивная пифагорова тройка единственным образом задается формулами

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

где \(m>n>0\), \(\gcd(m,n)=1\) и \(m\not\equiv n\pmod 2\). Поскольку треугольник прямоугольный, его минимальный охватывающий круг имеет радиус \(c/2\). Поэтому условие \(c<2R\) превращается в

$$m^2+n^2<2R.$$

Так как неравенство строгое, удобно ввести

$$T=2R-1,$$

после чего допустимые пары описываются просто условием

$$m^2+n^2\le T.$$

Для прямоугольного треугольника радиус вписанной окружности равен

$$r=\frac{a+b-c}{2},$$

а после подстановки формул Евклида получаем удивительно простую цель

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

Тем самым вся задача записывается как

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

Почему для каждого \(n\) нужен только один лучший партнер

При фиксированном \(n\) величина \(n(m-n)\) строго возрастает по \(m\). Следовательно, для данного \(n\) оптимальным может быть только наибольшее допустимое значение \(m\). Если временно забыть про четность и взаимную простоту, геометрическая верхняя граница имеет вид

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

Если \(m_{\max}(n)\le n\), то для этого \(n\) не существует ни одного допустимого треугольника. Иначе условия примитивности накладываются в единственно важном направлении: стартуем с \(m_{\max}(n)\), уменьшаем на \(1\), если \(m\) и \(n\) имеют одинаковую четность, а затем продолжаем идти вниз шагом \(2\), пока не получим \(\gcd(m,n)=1\). Первая пара, прошедшая эти проверки, уже является лучшей для данного \(n\), потому что любое более позднее значение \(m\) меньше и, значит, дает меньший радиус вписанной окружности.

Из этого сразу следует и естественная внешняя граница поиска. Так как в любой допустимой паре выполняется \(m>n\), имеем

$$2n^2<m^2+n^2\le T,$$

откуда

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

Непрерывная оболочка, делающая отсечение точным

Теперь ослабим арифметические ограничения и разрешим \(m\) принимать вещественное граничное значение \(m=\sqrt{T-n^2}\). Тогда любой целочисленный кандидат с тем же \(n\) удовлетворяет оценке

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

Эта функция является настоящей верхней границей, а не эвристическим показателем. Дифференцирование \(U(n)\) показывает, что ее единственный максимум достигается при соотношении

$$m=(1+\sqrt 2)\,n.$$

Эквивалентно, оптимальное вещественное значение \(n\) удовлетворяет

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

Значит, лучший треугольник нужно искать около одной непрерывной вершины, а не где-то произвольно по всему диапазону. Как только найден текущий рекорд, любой \(n\), для которого \(U(n)\) уже не превосходит этот рекорд, можно немедленно отбросить. Поскольку \(U(n)\) возрастает до единственного пика, а затем убывает, оставшаяся область есть один интервал вокруг \(n_0\), который удобно находить бисекцией слева и справа от вершины.

Разобранный пример: \(R=100\)

Здесь \(T=199\), поэтому условие допустимости имеет вид

$$m^2+n^2\le 199.$$

Непрерывный максимум находится примерно в точке

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

и это уже подсказывает, что лучшая целочисленная пара будет рядом с \(n=5\). Для \(n=4\) получаем

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

Пара \((13,4)\) уже имеет разную четность и \(\gcd(13,4)=1\), значит она дает

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

а потому

$$r=4(13-4)=36.$$

Поскольку \(c/2=92.5<100\), треугольник действительно помещается в круг. Близкие кандидаты чуть хуже: \(n=5\) ведет к \(m=12\) и \(r=35\), а \(n=6\) ведет к \(m=11\) и \(r=30\). Следовательно, \(F(100)=36\).

Как работает код

Общая стратегия поиска

Реализации на C++, Python и Java следуют одной и той же схеме. Сначала они вычисляют точные целочисленные квадратные корни, чтобы строгая граница \(m^2+n^2<2R\) безопасно обрабатывалась как \(m^2+n^2\le 2R-1\) без ошибок округления. Затем они оценивают положение непрерывного пика по формуле \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\), просматривают широкий интервал вокруг этой точки, быстро получая сильный начальный рекорд, и для каждого тестируемого \(n\) начинают с \(\lfloor\sqrt{2R-1-n^2}\rfloor\), при необходимости исправляют четность и идут вниз шагом \(2\), пока не найдут взаимно простого партнера.

Точное отсечение и финальный проход

После начального окна реализации используют оболочку \(U(n)=n(\sqrt{2R-1-n^2}-n)\), чтобы определить, какие значения \(n\) еще способны улучшить текущий лучший ответ. Так как \(U\) унимодальна, бисекция по обе стороны от пика выделяет ровно тот вещественный интервал, где улучшение еще возможно. После этого интервал переводится обратно в целые числа и слегка расширяется, чтобы исключить проблемы округления.

Внутри финального прохода код дополнительно применяет более острую целочисленную оценку

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

прежде чем выполнять вычисления gcd. Если даже эта величина не способна побить текущий рекорд, соответствующее \(n\) пропускается немедленно. Поэтому отсечение остается полностью точным: каждый отброшенный случай исключен доказанной верхней границей.

Анализ сложности

Естественная переменная поиска здесь равна \(n\), а весь допустимый диапазон имеет размер \(O(\sqrt R)\). Для каждого проверяемого \(n\) выполняются один целочисленный квадратный корень, возможно одна коррекция четности и затем спуск по значениям правильной четности, пока не встретится взаимно простой партнер. Значит, базовая структура алгоритма представляет собой поиск размера \(O(\sqrt R)\) с очень малой практической стоимостью на одного кандидата.

Непрерывная оценка \(U(n)\) сильно уменьшает реальное время работы по сравнению с полным перебором, потому что после нахождения хорошего рекорда активной остается лишь узкая полоса около пика. Память имеет порядок \(O(1)\): алгоритм хранит только небольшое число целочисленных и вещественных переменных.

Сноски и ссылки

  1. Страница задачи: https://projecteuler.net/problem=914
  2. Пифагоровы тройки: Wikipedia - Пифагоровы тройки
  3. Вписанная окружность: Wikipedia - Вписанная окружность
  4. Теорема Фалеса: Wikipedia - Теорема Фалеса
  5. Наибольший общий делитель: Wikipedia - Наибольший общий делитель
  6. Целочисленный квадратный корень: Wikipedia - Integer square root

ملخص المسألة

لأي نصف قطر موجب \(R\)، نرمز بـ \(F(R)\) إلى أكبر نصف قطر ممكن للدائرة الداخلية بين جميع المثلثات القائمة البدائية ذات الأضلاع الصحيحة التي تقع بالكامل وبصورة صارمة داخل دائرة نصف قطرها \(R\). إذا كان ضلعا القائمة هما \(a\) و\(b\)، وكان الوتر هو \(c\)، وكان نصف قطر الدائرة الداخلية هو \(r\)، فالمطلوب هو حساب \(F(10^{18})\).

القيد الهندسي أبسط كثيرا مما يبدو في البداية. فالمثلث القائم له نصف قطر دائرة محيطة يساوي \(c/2\)، ولذلك فإن شرط أن يقع داخل الدائرة بصورة صارمة يكافئ تماما الشرط \(c<2R\). وهكذا تتحول المسألة إلى تحسين على الثلاثيات الفيثاغورية البدائية. وتنجح التطبيقات في حلها عبر الجمع بين صيغة مغلقة دقيقة لـ \(r\)، وحجة رتابة عند تثبيت \(n\)، وحد علوي مستمر ودقيق يضغط مجال البحث إلى شريحة صغيرة.

المنهج الرياضي

من قيد الدائرة إلى معاملات إقليدس

كل ثلاثية فيثاغورية بدائية يمكن كتابتها بصورة وحيدة على الشكل

$$a=m^2-n^2,\qquad b=2mn,\qquad c=m^2+n^2,$$

حيث \(m>n>0\)، و\(\gcd(m,n)=1\)، و\(m\not\equiv n\pmod 2\). وبما أن المثلث قائم الزاوية، فإن أصغر دائرة تحيط به نصف قطرها \(c/2\). لذا يتحول الشرط \(c<2R\) إلى

$$m^2+n^2<2R.$$

ولأن المتباينة صارمة، فمن المناسب تعريف

$$T=2R-1,$$

بحيث تصبح الأزواج المقبولة هي بالضبط تلك التي تحقق

$$m^2+n^2\le T.$$

أما نصف قطر الدائرة الداخلية في المثلث القائم فيساوي

$$r=\frac{a+b-c}{2},$$

وبعد التعويض من صيغة إقليدس نحصل على الهدف البسيط

$$r=\frac{(m^2-n^2)+2mn-(m^2+n^2)}{2}=n(m-n).$$

ومن ثم تصبح المسألة كلها

$$F(R)=\max\left\{n(m-n):m>n>0,\ \gcd(m,n)=1,\ m\not\equiv n\pmod 2,\ m^2+n^2\le T\right\}.$$

لماذا يملك كل \(n\) شريكا أمثل واحدا

عند تثبيت \(n\)، تكون الكمية \(n(m-n)\) متزايدة تماما مع \(m\). لذلك لا يمكن أن يكون الحل الأمثل لذلك \(n\) إلا أكبر قيمة مسموح بها من \(m\). وإذا تجاهلنا مؤقتا شرط الفردية والزوجية وشرط التباين الأولي، فإن السقف الهندسي هو

$$m_{\max}(n)=\left\lfloor \sqrt{T-n^2}\right\rfloor.$$

إذا كان \(m_{\max}(n)\le n\)، فلا يوجد مثلث مقبول لهذا \(n\). وإلا فإن شروط البدائية تفرض في الاتجاه الوحيد المهم: نبدأ من \(m_{\max}(n)\)، وننقص مرة واحدة إذا كان \(m\) و\(n\) من الفردية نفسها، ثم نواصل النزول بخطوات مقدارها \(2\) حتى يصبح \(\gcd(m,n)=1\). وأول زوج ينجح في ذلك هو بالفعل الزوج الأمثل لذلك \(n\)، لأن أي مرشح لاحق ستكون له قيمة أصغر من \(m\) وبالتالي نصف قطر داخلي أصغر.

وهذا يعطينا أيضا الحد الخارجي الطبيعي للبحث. فبما أن كل زوج مقبول يحقق \(m>n\)، نحصل على

$$2n^2<m^2+n^2\le T,$$

ومن ثم

$$1\le n\le \left\lfloor \sqrt{\frac{T}{2}}\right\rfloor=\left\lfloor \sqrt{\frac{2R-1}{2}}\right\rfloor.$$

الغلاف المستمر الذي يجعل الاستبعاد دقيقا

الآن نرخي الشروط الحسابية، ونسمح لـ \(m\) أن يأخذ القيمة الحقيقية الواقعة على الحد \(m=\sqrt{T-n^2}\). عندها يحقق كل مرشح صحيح يشارك القيمة نفسها لـ \(n\) الحد الأعلى

$$r\le U(n):=n\left(\sqrt{T-n^2}-n\right).$$

هذه الدالة ليست تقديرا تقريبيا، بل حدا أعلى مثبتا فعلا. وباشتقاق \(U(n)\) يتبين أن لها قمة وحيدة تتحقق عندما

$$m=(1+\sqrt 2)\,n.$$

وبصورة مكافئة، فإن القيمة الحقيقية المثلى لـ \(n\) تحقق

$$n_0^2=\frac{T}{4+2\sqrt 2}=\frac{2R-1}{4+2\sqrt 2}.$$

وهذا يعني أن أفضل مثلث لا يوجد في مكان عشوائي من المجال كله، بل قرب قمة مستمرة واحدة. وما إن نحصل على أفضل قيمة حالية، حتى يمكن حذف كل \(n\) لا تتجاوز فيه \(U(n)\) تلك القيمة. وبما أن \(U(n)\) ترتفع إلى قمة واحدة ثم تنخفض، فإن المنطقة الباقية تكون فترة واحدة حول \(n_0\)، ويمكن تحديد طرفيها بدقة بواسطة البحث الثنائي على جانبي القمة.

مثال محلول: \(R=100\)

هنا يكون \(T=199\)، ولذلك يصبح شرط القبول

$$m^2+n^2\le 199.$$

تقع القمة المستمرة قرب

$$n_0\approx \sqrt{\frac{199}{4+2\sqrt 2}}\approx 5.40,$$

وهذا يوحي مباشرة بأن الحل الصحيح الأمثل سيقع قرب \(n=5\). عندما \(n=4\) نحصل على

$$m_{\max}(4)=\left\lfloor \sqrt{199-16}\right\rfloor=13.$$

والزوج \((13,4)\) يحقق بالفعل اختلاف الفردية والزوجية وكذلك \(\gcd(13,4)=1\)، ومن ثم ينتج

$$a=13^2-4^2=153,\qquad b=2\cdot 13\cdot 4=104,\qquad c=13^2+4^2=185,$$

وبالتالي

$$r=4(13-4)=36.$$

وبما أن \(c/2=92.5<100\)، فإن المثلث يقع فعلا داخل الدائرة. أما المرشحون القريبون فهم أضعف قليلا: عند \(n=5\) نحصل على \(m=12\) و\(r=35\)، وعند \(n=6\) نحصل على \(m=11\) و\(r=30\). إذن \(F(100)=36\).

كيف تعمل الشيفرة

استراتيجية البحث المشتركة

تتبع تطبيقات C++ وPython وJava المنطق نفسه. فهي تحسب أولا الجذر التربيعي الصحيح بدقة كاملة، بحيث يعالج الحد الصارم \(m^2+n^2<2R\) على صورة \(m^2+n^2\le 2R-1\) من دون أي خطأ ناتج عن التقريب العشري. ثم تقدر موضع القمة المستمرة بواسطة \(n_0\approx\sqrt{(2R-1)/(4+2\sqrt 2)}\)، وتمسح نافذة واسعة حول تلك النقطة لتكوين سجل أولي قوي بسرعة، ثم تقيم كل \(n\) مختبر بالبدء من \(\lfloor\sqrt{2R-1-n^2}\rfloor\)، وتصحيح الفردية والزوجية عند الحاجة، ثم النزول بخطوات مقدارها \(2\) حتى يظهر شريك أولي فيما بينه.

الاستبعاد الدقيق والمسح النهائي

بعد النافذة الأولى، تستخدم التطبيقات الغلاف \(U(n)=n(\sqrt{2R-1-n^2}-n)\) لتحديد قيم \(n\) التي ما زالت قادرة على تجاوز أفضل جواب حالي. وبما أن \(U\) أحادي القمة، فإن البحث الثنائي على جانبي القمة يعزل الفترة الحقيقية التي ما زال التحسن ممكنا داخلها. ثم تعاد هذه الفترة إلى مجال الأعداد الصحيحة مع توسيع طفيف حتى لا تضيع أي حالة بسبب التقريب.

داخل المسح النهائي تطبق الشيفرة أيضا الحد الصحيح الأقوى

$$n\left(\left\lfloor\sqrt{2R-1-n^2}\right\rfloor-n\right)$$

قبل الدخول في حسابات gcd. فإذا لم يكن حتى هذا الحد قادرا على تحسين السجل الحالي، فإن تلك القيمة من \(n\) تتجاوز مباشرة. وهكذا يبقى الاستبعاد دقيقا تماما، لأن كل حالة محذوفة قد استبعدت بحد أعلى مثبت.

تحليل التعقيد

المتغير الطبيعي للبحث هو \(n\)، ومجاله المقبول الكامل حجمه \(O(\sqrt R)\). ولكل قيمة يجري اختبارها من \(n\)، تنفذ الشيفرة جذرا تربيعيا صحيحا واحدا، وربما تصحيحا بسيطا للفردية والزوجية، ثم نزولا عبر القيم ذات الفردية المناسبة حتى الوصول إلى شريك أولي فيما بينه. ولذلك فالبنية الأساسية هي بحث بحجم \(O(\sqrt R)\) مع كلفة صغيرة جدا لكل مرشح في التطبيق العملي.

أما الغلاف المستمر \(U(n)\) فيخفض زمن التنفيذ الفعلي كثيرا مقارنة بالمسح الكامل، لأن ما يبقى بعد الحصول على سجل جيد هو نطاق ضيق فقط حول القمة. الذاكرة المستعملة هي \(O(1)\)، إذ لا يخزن البرنامج إلا عددا قليلا من المتغيرات الصحيحة والعشرية.

هوامش ومراجع

  1. صفحة المسألة: https://projecteuler.net/problem=914
  2. ثلاثية فيثاغورية: Wikipedia - ثلاثية فيثاغورية
  3. الدائرة الداخلية: Wikipedia - الدائرة الداخلية والمحيطة
  4. مبرهنة طاليس: Wikipedia - مبرهنة طاليس
  5. القاسم المشترك الأكبر: Wikipedia - القاسم المشترك الأكبر
  6. الجذر التربيعي الصحيح: Wikipedia - Integer square root