Problem Summary

In base \(14\), a positive integer \(x\) is a steady square of length \(n\) if its square ends in the same \(n\) base-\(14\) digits, i.e.

$$x^2 \equiv x \pmod{14^n}.$$

The code sums the base-\(14\) digit sums of all positive steady squares with at most \(n_{\max}\) digits and prints the final total in base \(14\). The Project Euler final value is intentionally omitted here.

Mathematical Approach

1) Idempotents modulo \(14^n\). The congruence \(x^2 \equiv x \pmod{14^n}\) is equivalent to

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

Because \(\gcd(x,x-1)=1\), for the factor \(2^n\) one of \(x\) and \(x-1\) must be divisible by \(2^n\), so

$$x \equiv 0 \text{ or } 1 \pmod{2^n}.$$

The same argument modulo \(7^n\) gives

$$x \equiv 0 \text{ or } 1 \pmod{7^n}.$$

By the Chinese Remainder Theorem there are exactly four idempotent residue classes modulo \(14^n\):

$$ (0,0),\quad (1,1),\quad (0,1),\quad (1,0) $$

with respect to \((\bmod\,2^n,\bmod\,7^n)\). For \(n=1\) these are exactly

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

The branches starting at \(0\) and \(1\) are trivial; the two nontrivial infinite branches start at \(7\) and \(8\). That is why the implementation only tracks those two sequences, while adding the one-digit steady square \(1\) separately.

2) One-step lifting in base \(14\). Suppose \(x\) is already an idempotent modulo

$$M=14^k,$$

so that

$$x^2-x=fM$$

for some integer \(f\). Any lift to modulus \(14M=14^{k+1}\) has the form

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

Expanding gives

$$x'^2-x'=(x^2-x)+(2x-1)tM+t^2M^2=M\bigl(f+(2x-1)t+t^2M\bigr).$$

For divisibility by \(14M\), the bracket must vanish modulo \(14\). Since \(M\) is already a multiple of \(14\), the term \(t^2M\) disappears modulo \(14\), so we only need

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) Why the next digit is unique. For an idempotent, \(x\equiv 0\) or \(1\pmod 2\) and also \(x\equiv 0\) or \(1\pmod 7\). Therefore

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

so \(\gcd(2x-1,14)=1\). Hence \(2x-1\) has a unique inverse modulo \(14\), and the congruence above determines one and only one digit \(t\in\{0,\dots,13\}\). This is the whole reason the algorithm has only one successor per branch at each step.

4) The carry-like state update. After choosing \(t\), divide the bracket by \(14\):

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

Then the lifted value satisfies

$$x'^2-x'=f'(14M).$$

So the state for the next step is again just \((x',f',14M)\). This is why the implementation can advance each branch using constant-time arithmetic per length.

5) Why \(t\) is the new leading digit. Because \(0\le x<M=14^k\), the number \(x\) occupies at most \(k\) base-\(14\) digits. Adding \(tM=t\cdot 14^k\) inserts one new digit to the left. Therefore the lift

$$x'=x+t14^k$$

has base-\(14\) representation obtained by prefixing the old \(k\)-digit block with the new digit \(t\).

If \(t=0\), then the lifted idempotent is still valid modulo \(14^{k+1}\), but it is not a genuine \((k+1)\)-digit positive number. That is exactly why the code stores leading_digit and ignores states with leading digit \(0\) when counting steady squares of a fixed length.

6) Concrete branch examples. Starting from the nontrivial roots modulo \(14\):

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

and

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots$$

The value \(0\mathrm{c37}\) is a valid idempotent modulo \(14^4\), but its new leading digit is \(0\), so it is not counted as a \(4\)-digit steady square. The branch rooted at \(1\) always lifts with \(t=0\), so it only contributes the one-digit number \(1\).

7) Digit-sum accumulation. If a branch currently has digit sum \(\sigma\) and the next leading digit is \(t\), then the new digit sum is simply

$$\sigma'=\sigma+t.$$

So the code never recomputes digit sums from scratch; it just keeps one running total for the \(7\)-branch and one for the \(8\)-branch. The overall answer starts from

$$1$$

to count the steady square \(1\), then adds the digit sum of each nonzero-leading branch at every length.

Worked Checkpoints

For the first few lengths, the positive steady squares are:

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

The cumulative digit-sum totals are therefore

$$16,\ 44,\ 85,\ 117,\dots$$

and the source file checks that

$$S(9)=582,$$

which is

$$582_{10}=2\mathrm{d}8_{14}.$$

It also verifies directly during the first 20 lifts that each tracked branch still satisfies \(x^2\equiv x\pmod{14^n}\).

Complexity Analysis

There are only two nontrivial active branches, and each step performs a constant amount of modular arithmetic and big-integer updates. Therefore the time complexity is

$$O(n_{\max}),$$

and the extra memory is

$$O(1).$$

The values of \(x\) themselves become enormous, which is why the code uses cpp_int, but the state space never branches.

Further Reading

  1. Problem page: https://projecteuler.net/problem=284
  2. Chinese remainder theorem: https://en.wikipedia.org/wiki/Chinese_remainder_theorem
  3. Hensel lifting intuition: https://en.wikipedia.org/wiki/Hensel%27s_lemma

Problemzusammenfassung

In Basis \(14\) heißt eine positive Zahl \(x\) ein steady square der Länge \(n\), wenn ihr Quadrat auf dieselben \(n\) Ziffern endet, also

$$x^2 \equiv x \pmod{14^n}.$$

Der Code summiert die Ziffernsummen aller positiven steady squares mit höchstens \(n_{\max}\) Stellen und gibt das Endergebnis wieder in Basis \(14\) aus. Der endgültige Project-Euler-Wert wird hier absichtlich nicht ausgeschrieben.

Mathematischer Ansatz

1) Idempotente modulo \(14^n\). Die Kongruenz \(x^2 \equiv x \pmod{14^n}\) ist äquivalent zu

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

Wegen \(\gcd(x,x-1)=1\) muss modulo \(2^n\) einer der Faktoren vollständig durch \(2^n\) teilbar sein, also

$$x \equiv 0 \text{ oder } 1 \pmod{2^n}.$$

Dasselbe gilt modulo \(7^n\):

$$x \equiv 0 \text{ oder } 1 \pmod{7^n}.$$

Nach dem chinesischen Restsatz gibt es daher genau vier idempotente Klassen modulo \(14^n\). Für \(n=1\) sind das

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

Die Äste \(0\) und \(1\) sind trivial; die beiden nichttrivialen unendlichen Äste starten bei \(7\) und \(8\). Deshalb verfolgt die Implementierung nur diese beiden Folgen und addiert die einstellige steady square \(1\) separat.

2) Ein Hebungsschritt in Basis \(14\). Angenommen, \(x\) ist bereits idempotent modulo

$$M=14^k,$$

also

$$x^2-x=fM.$$

Jede Hebung auf Modulus \(14M=14^{k+1}\) hat die Form

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

Ausmultiplizieren liefert

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr).$$

Für Teilbarkeit durch \(14M\) muss der Klammerausdruck modulo \(14\) verschwinden. Da \(M\) selbst bereits ein Vielfaches von \(14\) ist, verschwindet \(t^2M\) modulo \(14\), und es bleibt nur

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) Warum die nächste Ziffer eindeutig ist. Für ein idempotentes \(x\) gilt modulo \(2\) und modulo \(7\) jeweils \(x\equiv 0\) oder \(1\). Also ist

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

und damit \(\gcd(2x-1,14)=1\). Somit besitzt \(2x-1\) ein Inverses modulo \(14\), und die lineare Kongruenz bestimmt genau eine Ziffer \(t\). Darum hat jeder Ast auf jeder Stufe genau einen Nachfolger.

4) Der Carry-Zustand. Nach Wahl von \(t\) setzt man

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

Dann gilt wieder

$$x'^2-x'=f'(14M).$$

Der nächste Zustand ist also erneut nur \((x',f',14M)\). Genau deshalb reicht pro Länge konstante Arbeit.

5) Warum \(t\) die neue führende Ziffer ist. Da \(0\le x<14^k\), belegt \(x\) höchstens \(k\) Basis-\(14\)-Stellen. Das Addieren von \(t14^k\) setzt eine neue Ziffer links davor. Wenn \(t=0\), bleibt zwar eine gültige idempotente Klasse modulo \(14^{k+1}\), aber keine echte positive \((k+1)\)-stellige Zahl. Deshalb speichert der Code leading_digit und zählt einen Ast für diese Länge nur dann, wenn die neue führende Ziffer ungleich \(0\) ist.

6) Konkrete Astbeispiele. Von den beiden nichttrivialen Wurzeln aus erhält man

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

und

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

\(0\mathrm{c37}\) ist modulo \(14^4\) zwar korrekt idempotent, wird aber nicht als \(4\)-stellige Zahl gezählt. Der Ast von \(1\) hebt sich immer mit \(t=0\) und liefert daher nur die einstellige Zahl \(1\).

7) Akkumulation der Ziffernsummen. Hat ein Ast aktuell Ziffernsumme \(\sigma\) und die neue führende Ziffer ist \(t\), dann ist

$$\sigma'=\sigma+t.$$

Die Implementierung berechnet Ziffernsummen also nie neu, sondern führt nur laufende Summen entlang des \(7\)- und des \(8\)-Astes. Die Gesamtsumme startet bei \(1\), um die steady square \(1\) mitzuzählen.

Arbeitsbeispiele und Checkpoints

Für die ersten Längen sind die positiven steady squares:

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

Die kumulierten Ziffernsummen lauten daher

$$16,\ 44,\ 85,\ 117,\dots$$

und im Quelltext wird überprüft, dass

$$S(9)=582=2\mathrm{d}8_{14}.$$

Zusätzlich wird in den ersten 20 Hebungsschritten direkt getestet, dass weiterhin \(x^2\equiv x\pmod{14^n}\) gilt.

Komplexitätsanalyse

Es gibt nur zwei aktive nichttriviale Äste, und pro Länge wird auf jedem Ast nur konstante modulare Arithmetik ausgeführt. Daher beträgt die Laufzeit

$$O(n_{\max}),$$

und der Zusatzspeicher ist

$$O(1).$$

Die Zahlen \(x\) selbst werden sehr groß, deshalb verwendet der Code cpp_int, aber der Zustandsraum verzweigt nie.

Weiterführende Hinweise

  1. Problemseite: https://projecteuler.net/problem=284
  2. Chinesischer Restsatz: https://de.wikipedia.org/wiki/Chinesischer_Restsatz
  3. Henselsches Lemma: https://de.wikipedia.org/wiki/Henselsches_Lemma

Problem Özeti

14 tabanında pozitif bir \(x\) sayısı, karesinin son \(n\) basamağı yine \(x\)'in kendisiyle aynıysa bir steady square olarak adlandırılır. Yani

$$x^2 \equiv x \pmod{14^n}.$$

Kod, en fazla \(n_{\max}\) basamaklı tüm pozitif steady square sayıların 14 tabanındaki basamak toplamlarını toplayıp sonucu yine 14 tabanında yazdırır. Nihai Project Euler cevabı burada bilinçli olarak verilmez.

Matematiksel Yaklaşım

1) \(14^n\) altında idempotent sınıflar. \(x^2 \equiv x \pmod{14^n}\) koşulu

$$x(x-1)\equiv 0 \pmod{2^n7^n}$$

ile aynıdır. \(\gcd(x,x-1)=1\) olduğundan, \(2^n\) çarpanı ya tamamen \(x\)'e ya da tamamen \(x-1\)'e gider. Dolayısıyla

$$x \equiv 0 \text{ veya } 1 \pmod{2^n}.$$

Aynı mantık \(7^n\) için de geçerlidir:

$$x \equiv 0 \text{ veya } 1 \pmod{7^n}.$$

Çin kalan teoremi yüzünden her \(n\) için tam dört idempotent sınıf vardır. \(n=1\) durumunda bunlar

$$0,\ 1,\ 7,\ 8 \pmod{14}$$

olarak görünür. \(0\) ve \(1\) dalları triviyaldir; sonsuza uzayan gayri-trivial iki dal \(7\) ve \(8\)'den başlar. Kodun esasen bu iki dalı izlemesinin nedeni budur; tek basamaklı steady square \(1\) ise ayrıca eklenir.

2) 14 tabanında tek adım lift. Diyelim ki \(x\),

$$M=14^k$$

modülünde zaten idempotent olsun ve

$$x^2-x=fM$$

olsun. \(14^{k+1}\) modülündeki her lift

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}$$

şeklindedir. Açarsak

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr)$$

çıkar. Bunun \(14M\)'ye bölünebilmesi için parantezin mod \(14\)'te sıfır olması gerekir. \(M\) zaten \(14\)'ün katı olduğu için \(t^2M\) terimi mod \(14\)'te kaybolur ve elimizde sadece

$$f+(2x-1)t\equiv 0 \pmod{14}$$

kalır.

3) Neden sonraki basamak tektir? Bir idempotent için \(x\), hem mod \(2\) hem mod \(7\) altında \(0\) veya \(1\)'dir. Bu yüzden

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

yani \(\gcd(2x-1,14)=1\). Demek ki \(2x-1\)'in mod \(14\)'te tersi vardır ve yukarıdaki lineer kongruans \(t\) basamağını tekil olarak belirler. Her dalın her seviyede tam bir devamı olmasının sebebi budur.

4) Carry benzeri durum güncellemesi. \(t\) seçildikten sonra

$$f'=\frac{f+(2x-1)t+t^2M}{14}$$

tanımlanır. Böylece

$$x'^2-x'=f'(14M)$$

olur. Yani bir sonraki adım için gereken tek şey yine \((x',f',14M)\) durumudur. Bu yüzden algoritma her uzunluk artışında sabit iş yapar.

5) Neden \(t\) yeni en anlamlı basamaktır? Çünkü \(0\le x<14^k\). O halde \(x\), en fazla \(k\) basamaklı bir 14 tabanlı bloktur. \(t14^k\) eklemek, bu bloğun soluna bir yeni basamak koyar. Eğer \(t=0\) ise yeni modülde geçerli bir idempotent elde ederiz, ama bu gerçek bir \((k+1)\)-basamaklı pozitif sayı değildir. Kodun leading_digit tutup sadece sıfır olmayanları saymasının nedeni budur.

6) Somut dal örnekleri. İki gayri-trivial kökten gelen diziler şöyledir:

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

ve

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

\(0\mathrm{c37}\), \(14^4\) altında geçerli bir idempotenttir ama yeni üst basamağı \(0\) olduğu için \(4\) basamaklı steady square olarak sayılmaz. \(1\) kökünden gelen dal ise her seferinde \(t=0\) ile yükselir; dolayısıyla yalnızca tek basamaklı \(1\) katkı verir.

7) Basamak toplamı biriktirme. Bir dalın mevcut basamak toplamı \(\sigma\) ve yeni üst basamağı \(t\) ise yeni toplam

$$\sigma'=\sigma+t$$

olur. Bu yüzden kod her seferinde rakamları yeniden çözmez; \(7\)-dalı ve \(8\)-dalı için yalnızca birer kümülatif toplam tutar. Genel toplam da steady square \(1\)'i saymak için başlangıçta

$$1$$

değerinden başlar.

Çalışan Kontroller

İlk birkaç uzunluk için pozitif steady square sayılar şunlardır:

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

Dolayısıyla kümülatif basamak toplamları

$$16,\ 44,\ 85,\ 117,\dots$$

şeklinde gider ve kaynak dosya şu checkpoint'i doğrular:

$$S(9)=582=2\mathrm{d}8_{14}.$$

Ayrıca ilk 20 lift boyunca her dal için doğrudan \(x^2\equiv x\pmod{14^n}\) kontrolü de yapılır.

Karmaşıklık Analizi

Sadece iki aktif gayri-trivial dal vardır ve her uzunluk adımında her dal için sabit sayıda modüler aritmetik işlemi yapılır. Bu nedenle süre karmaşıklığı

$$O(n_{\max})$$

ve ek bellek karmaşıklığı

$$O(1)$$

olur. \(x\) sayıları çok büyüdüğü için kod cpp_int kullanır; fakat durum uzayı hiç dallanmaz.

İleri Okuma

  1. Problem sayfası: https://projecteuler.net/problem=284
  2. Çin kalan teoremi: https://tr.wikipedia.org/wiki/Çin_kalan_teoremi
  3. Hensel lemması sezgisi: https://en.wikipedia.org/wiki/Hensel%27s_lemma

Resumen del Problema

En base \(14\), un entero positivo \(x\) es un steady square de longitud \(n\) si su cuadrado termina en los mismos \(n\) dígitos base \(14\), es decir, si

$$x^2 \equiv x \pmod{14^n}.$$

El programa suma las sumas de dígitos en base \(14\) de todos los steady squares positivos con a lo sumo \(n_{\max}\) dígitos y escribe el total final en base \(14\). El valor final de Project Euler se omite aquí a propósito.

Enfoque Matemático

1) Idempotentes módulo \(14^n\). La congruencia \(x^2 \equiv x \pmod{14^n}\) equivale a

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

Como \(\gcd(x,x-1)=1\), módulo \(2^n\) debe ocurrir

$$x \equiv 0 \text{ o } 1 \pmod{2^n},$$

y del mismo modo, módulo \(7^n\),

$$x \equiv 0 \text{ o } 1 \pmod{7^n}.$$

Por el teorema chino del resto hay exactamente cuatro clases idempotentes módulo \(14^n\). Para \(n=1\) son

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

Las ramas \(0\) y \(1\) son triviales; las dos ramas infinitas no triviales empiezan en \(7\) y \(8\). Por eso la implementación sigue solo esas dos secuencias y añade aparte el steady square de un dígito \(1\).

2) Un paso de elevación en base \(14\). Supongamos que \(x\) ya es idempotente módulo

$$M=14^k,$$

de modo que

$$x^2-x=fM.$$

Cualquier elevación al módulo \(14M=14^{k+1}\) tiene la forma

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

Al expandir, obtenemos

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr).$$

Para que esto sea divisible por \(14M\), el paréntesis debe anularse módulo \(14\). Como \(M\) ya es múltiplo de \(14\), el término \(t^2M\) desaparece módulo \(14\), y queda

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) Por qué el siguiente dígito es único. Para un idempotente, \(x\equiv 0\) o \(1\) tanto módulo \(2\) como módulo \(7\). Luego

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

así que \(\gcd(2x-1,14)=1\). Por tanto \(2x-1\) tiene inverso módulo \(14\) y la congruencia lineal determina exactamente un dígito \(t\). Esa es la razón de que cada rama tenga un solo sucesor por nivel.

4) El estado tipo carry. Después de elegir \(t\), definimos

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

Entonces

$$x'^2-x'=f'(14M),$$

de modo que el siguiente estado vuelve a ser simplemente \((x',f',14M)\). Por eso el coste por longitud es constante.

5) Por qué \(t\) es el nuevo dígito más significativo. Como \(0\le x<14^k\), \(x\) ocupa a lo sumo \(k\) dígitos en base \(14\). Sumar \(t14^k\) antepone exactamente un nuevo dígito. Si \(t=0\), el idempotente elevado sigue siendo válido módulo \(14^{k+1}\), pero ya no es un número positivo genuino de \((k+1)\) dígitos. Por eso el código guarda leading_digit y solo cuenta un estado cuando ese nuevo dígito es distinto de \(0\).

6) Ejemplos concretos de ramas. A partir de las dos raíces no triviales:

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

y

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

El valor \(0\mathrm{c37}\) es un idempotente válido módulo \(14^4\), pero no cuenta como steady square de 4 dígitos porque su nuevo dígito principal es \(0\). La rama que nace en \(1\) siempre eleva con \(t=0\), así que solo aporta el número de un dígito \(1\).

7) Acumulación de la suma de dígitos. Si una rama tiene suma actual \(\sigma\) y el nuevo dígito es \(t\), entonces

$$\sigma'=\sigma+t.$$

El programa no recalcula sumas de dígitos desde cero; mantiene una suma acumulada para la rama de \(7\) y otra para la de \(8\). El total global arranca en \(1\) para contar el steady square \(1\).

Ejemplos y Checkpoints

En las primeras longitudes, los steady squares positivos son

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

Por tanto, los totales acumulados de suma de dígitos son

$$16,\ 44,\ 85,\ 117,\dots$$

y el archivo comprueba que

$$S(9)=582=2\mathrm{d}8_{14}.$$

Además verifica durante las primeras 20 elevaciones que en ambas ramas sigue cumpliéndose \(x^2\equiv x\pmod{14^n}\).

Complejidad

Solo hay dos ramas activas no triviales, y en cada longitud se realiza una cantidad constante de aritmética modular y actualizaciones de enteros grandes. Por tanto, el tiempo es

$$O(n_{\max})$$

y la memoria extra es

$$O(1).$$

Los valores de \(x\) crecen muchísimo, por eso el código usa cpp_int, pero el espacio de estados nunca se ramifica.

Lecturas

  1. Página del problema: https://projecteuler.net/problem=284
  2. Teorema chino del resto: https://es.wikipedia.org/wiki/Teorema_chino_del_resto
  3. Lema de Hensel: https://es.wikipedia.org/wiki/Lema_de_Hensel

问题概述

在 \(14\) 进制中,若一个正整数 \(x\) 的平方最后 \(n\) 位仍然是它自己,也就是

$$x^2 \equiv x \pmod{14^n},$$

那么它就是一个长度为 \(n\) 的 steady square。程序要做的是:把所有位数不超过 \(n_{\max}\) 的正 steady square 的 \(14\) 进制数位和全部加起来,并把最后的总和用 \(14\) 进制输出。最终 Project Euler 数值答案这里不展示。

数学方法

1) 模 \(14^n\) 下的幂等元。 条件 \(x^2 \equiv x \pmod{14^n}\) 等价于

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

由于 \(\gcd(x,x-1)=1\),所以在模 \(2^n\) 下必有

$$x \equiv 0 \text{ 或 } 1 \pmod{2^n},$$

在模 \(7^n\) 下同样有

$$x \equiv 0 \text{ 或 } 1 \pmod{7^n}.$$

由中国剩余定理可知,模 \(14^n\) 恰好有四个幂等类。对 \(n=1\) 来说,它们正是

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

其中 \(0\) 和 \(1\) 两条分支是平凡的;真正需要追踪的无限分支只有从 \(7\) 和 \(8\) 出发的两条。所以代码只维护这两条序列,并把一位数 steady square \(1\) 单独加入。

2) 14 进制的一步提升。 假设 \(x\) 已经在

$$M=14^k$$

模数下满足幂等,即

$$x^2-x=fM.$$

那么提升到模 \(14M=14^{k+1}\) 的候选一定形如

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

展开得到

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr).$$

要让它被 \(14M\) 整除,括号内必须在模 \(14\) 下为零。由于 \(M\) 本身已是 \(14\) 的倍数,项 \(t^2M\) 在模 \(14\) 下消失,于是只剩

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) 为什么下一位是唯一的。 对幂等元来说,\(x\) 在模 \(2\) 和模 \(7\) 下都只能是 \(0\) 或 \(1\)。因此

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

从而 \(\gcd(2x-1,14)=1\)。所以 \(2x-1\) 在模 \(14\) 下可逆,上面的线性同余恰好唯一确定一个新数位 \(t\)。这就是为什么每条分支在每一层只有一个后继。

4) 类似 carry 的状态更新。 选定 \(t\) 后,定义

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

于是有

$$x'^2-x'=f'(14M).$$

也就是说,下一层状态仍然只需要 \((x',f',14M)\) 这三个量。每增加一位时的工作量因此是常数级。

5) 为什么 \(t\) 就是新出现的最高位。 因为 \(0\le x<14^k\),所以 \(x\) 最多只有 \(k\) 位 \(14\) 进制数字。加上 \(t14^k\) 以后,就相当于在左边添了一位新数字 \(t\)。如果 \(t=0\),那么得到的仍然是模 \(14^{k+1}\) 下的有效幂等元,但它并不是一个真正的 \((k+1)\) 位正数。这就是代码为什么保存 leading_digit,并且只在它非零时才把该状态计入这一位数长度。

6) 具体分支示例。 从两个非平凡根开始,序列分别是

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

以及

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

\(0\mathrm{c37}\) 虽然是模 \(14^4\) 下的合法幂等元,但由于新最高位是 \(0\),所以不算作一个真正的 \(4\) 位 steady square。根为 \(1\) 的分支则始终取 \(t=0\),因此只贡献一位数 \(1\)。

7) 数位和如何累计。 如果某条分支当前的数位和是 \(\sigma\),新最高位是 \(t\),那么新的数位和就是

$$\sigma'=\sigma+t.$$

因此程序不需要反复重新计算数位和,只要分别维护 \(7\) 分支和 \(8\) 分支的运行和即可。总和初值从

$$1$$

开始,用来计入 steady square \(1\)。

工作示例与 Checkpoint

前几种长度下的正 steady square 为

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

因此累计数位和依次为

$$16,\ 44,\ 85,\ 117,\dots$$

源码还验证了

$$S(9)=582=2\mathrm{d}8_{14}.$$

另外,它也直接检查前 20 次提升后,两条分支都仍然满足 \(x^2\equiv x\pmod{14^n}\)。

复杂度分析

只有两条非平凡活动分支,每增加一位只进行常数次模运算和大整数更新。因此时间复杂度为

$$O(n_{\max}),$$

额外空间复杂度为

$$O(1).$$

由于 \(x\) 本身会增长到极大,代码使用了 cpp_int,但状态空间始终不分叉。

延伸阅读

  1. 题目页面: https://projecteuler.net/problem=284
  2. 中国剩余定理: https://zh.wikipedia.org/wiki/中国剩余定理
  3. Hensel 引理: https://zh.wikipedia.org/wiki/Hensel引理

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

В системе счисления по основанию \(14\) положительное число \(x\) называется steady square длины \(n\), если квадрат числа оканчивается теми же \(n\) цифрами, то есть

$$x^2 \equiv x \pmod{14^n}.$$

Программа суммирует суммы цифр в базе \(14\) для всех положительных steady square с не более чем \(n_{\max}\) цифрами и печатает итог тоже в базе \(14\). Окончательный числовой ответ Project Euler здесь намеренно не приводится.

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

1) Идемпотенты по модулю \(14^n\). Условие \(x^2 \equiv x \pmod{14^n}\) эквивалентно

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

Так как \(\gcd(x,x-1)=1\), по модулю \(2^n\) обязательно

$$x \equiv 0 \text{ или } 1 \pmod{2^n},$$

и точно так же по модулю \(7^n\)

$$x \equiv 0 \text{ или } 1 \pmod{7^n}.$$

По китайской теореме об остатках отсюда следует, что по модулю \(14^n\) существует ровно четыре идемпотентных класса. При \(n=1\) это

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

Ветви \(0\) и \(1\) тривиальны; две нетривиальные бесконечные ветви стартуют из \(7\) и \(8\). Поэтому код отслеживает только эти две последовательности, а одноцифровое steady square \(1\) добавляет отдельно.

2) Один шаг подъема в базе \(14\). Пусть \(x\) уже идемпотентно по модулю

$$M=14^k,$$

то есть

$$x^2-x=fM.$$

Любой подъем на модуль \(14M=14^{k+1}\) имеет вид

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

После раскрытия скобок получаем

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr).$$

Чтобы это делилось на \(14M\), выражение в скобках должно быть нулем по модулю \(14\). Так как \(M\) уже кратно \(14\), член \(t^2M\) исчезает по модулю \(14\), и остается только

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) Почему следующая цифра определяется однозначно. Для идемпотента число \(x\) по модулю \(2\) и по модулю \(7\) равно либо \(0\), либо \(1\). Значит,

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

поэтому \(\gcd(2x-1,14)=1\). Следовательно, \(2x-1\) обратимо по модулю \(14\), и линейная конгруэнция определяет ровно одну цифру \(t\). Именно поэтому на каждом уровне у ветви есть единственный потомок.

4) Состояние типа переноса. После выбора \(t\) вводится

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

Тогда снова верно

$$x'^2-x'=f'(14M).$$

То есть следующее состояние опять описывается только тройкой \((x',f',14M)\). Поэтому стоимость на один шаг длины постоянна.

5) Почему \(t\) есть новая старшая цифра. Поскольку \(0\le x<14^k\), число \(x\) занимает не более \(k\) цифр в базе \(14\). Прибавление \(t14^k\) просто дописывает слева новую цифру \(t\). Если \(t=0\), то мы по-прежнему получаем корректный идемпотент по модулю \(14^{k+1}\), но это уже не настоящее положительное \((k+1)\)-значное число. Именно поэтому код хранит leading_digit и учитывает состояние для фиксированной длины только при ненулевой новой старшей цифре.

6) Конкретные примеры ветвей. Из двух нетривиальных корней получаются последовательности

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

и

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

Число \(0\mathrm{c37}\) является правильным идемпотентом по модулю \(14^4\), но не считается как четырехзначное steady square, потому что его новая старшая цифра равна \(0\). Ветвь, идущая от \(1\), всегда поднимается с \(t=0\), поэтому дает вклад только одноцифровым числом \(1\).

7) Накопление суммы цифр. Если у ветви текущая сумма цифр равна \(\sigma\), а новая старшая цифра равна \(t\), то

$$\sigma'=\sigma+t.$$

Поэтому программа не пересчитывает сумму цифр заново, а хранит одну накопленную сумму для ветви \(7\) и одну для ветви \(8\). Общий итог начинается с

$$1$$

для учета steady square \(1\).

Работающие примеры и проверки

Для первых длин положительные steady square имеют вид

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

Соответствующие накопленные суммы цифр равны

$$16,\ 44,\ 85,\ 117,\dots$$

и в исходном файле проверяется, что

$$S(9)=582=2\mathrm{d}8_{14}.$$

Дополнительно код проверяет в первых 20 подъемах, что обе отслеживаемые ветви действительно сохраняют свойство \(x^2\equiv x\pmod{14^n}\).

Сложность

Существуют только две активные нетривиальные ветви, и на каждом шаге длины выполняется постоянное число модульных операций и обновлений больших целых. Поэтому время работы равно

$$O(n_{\max}),$$

а дополнительная память

$$O(1).$$

Сами числа \(x\) становятся огромными, поэтому используется cpp_int, но пространство состояний не разветвляется.

Дополнительные материалы

  1. Страница задачи: https://projecteuler.net/problem=284
  2. Китайская теорема об остатках: https://ru.wikipedia.org/wiki/Китайская_теорема_об_остатках
  3. Лемма Гензеля: https://ru.wikipedia.org/wiki/Лемма_Гензеля

ملخص المسألة

في الأساس \(14\)، يسمى العدد الموجب \(x\) steady square بطول \(n\) إذا كان مربعه ينتهي بنفس آخر \(n\) أرقام من تمثيله، أي إذا تحقق

$$x^2 \equiv x \pmod{14^n}.$$

البرنامج يجمع مجاميع الأرقام في الأساس \(14\) لكل steady square موجب له على الأكثر \(n_{\max}\) رقمًا، ثم يطبع المجموع النهائي بالأساس \(14\). القيمة العددية النهائية لمسألة Project Euler لا نعرضها هنا عمدًا.

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

1) العناصر idempotent بترديد \(14^n\). الشرط \(x^2 \equiv x \pmod{14^n}\) يكافئ

$$x(x-1)\equiv 0 \pmod{2^n7^n}.$$

وبما أن \(\gcd(x,x-1)=1\)، فلا بد أن يكون أحد العددين \(x\) أو \(x-1\) قابلًا للقسمة على \(2^n\)، أي

$$x \equiv 0 \text{ أو } 1 \pmod{2^n}.$$

وبالطريقة نفسها نحصل على

$$x \equiv 0 \text{ أو } 1 \pmod{7^n}.$$

وبحسب مبرهنة الباقي الصيني توجد بالضبط أربع فئات idempotent modulo \(14^n\). وعندما \(n=1\) تكون هذه الفئات هي

$$0,\ 1,\ 7,\ 8 \pmod{14}.$$

الفرعان \(0\) و\(1\) تافهان، أما الفرعان غير التافهين اللامتناهيان فيبدآن من \(7\) و\(8\). لذلك يتتبع التنفيذ هذين الفرعين فقط، بينما يضيف steady square أحادي الرقم \(1\) بشكل منفصل.

2) خطوة رفع واحدة في الأساس \(14\). افترض أن \(x\) يحقق خاصية idempotent modulo

$$M=14^k,$$

أي أن

$$x^2-x=fM.$$

وأي رفع إلى modulo \(14M=14^{k+1}\) يكون من الشكل

$$x'=x+tM,\qquad t\in\{0,1,\dots,13\}.$$

بالتوسيع نحصل على

$$x'^2-x'=M\bigl(f+(2x-1)t+t^2M\bigr).$$

ولكي يقبل القسمة على \(14M\)، يجب أن يساوي ما داخل القوس صفرًا modulo \(14\). وبما أن \(M\) نفسه من مضاعفات \(14\)، فإن الحد \(t^2M\) يختفي modulo \(14\)، ويبقى فقط

$$f+(2x-1)t\equiv 0 \pmod{14}.$$

3) لماذا الرقم التالي وحيد. إذا كان \(x\) idempotent، فهو يساوي \(0\) أو \(1\) modulo \(2\) وكذلك modulo \(7\). لذا

$$2x-1\equiv \pm 1 \pmod 2,\qquad 2x-1\equiv \pm 1 \pmod 7,$$

ومن ثم \(\gcd(2x-1,14)=1\). هذا يعني أن \(2x-1\) قابل للعكس modulo \(14\)، ولذلك تحدد المعادلة الخطية السابقة رقمًا وحيدًا \(t\). لهذا السبب يملك كل فرع تابعًا واحدًا فقط في كل مستوى.

4) تحديث حالة شبيه بالحمل. بعد اختيار \(t\)، نعرف

$$f'=\frac{f+(2x-1)t+t^2M}{14}.$$

وعندها يصبح

$$x'^2-x'=f'(14M).$$

إذًا الحالة التالية لا تحتاج إلا إلى الثلاثي \((x',f',14M)\) مرة أخرى، ولهذا تبقى الكلفة لكل طول ثابتة.

5) لماذا يمثل \(t\) الرقم الأعلى الجديد. لأن \(0\le x<14^k\)، فإن \(x\) يشغل على الأكثر \(k\) أرقام في الأساس \(14\). وإضافة \(t14^k\) تعني وضع رقم جديد إلى يسار الكتلة القديمة. إذا كان \(t=0\)، فإننا نحصل على idempotent صحيح modulo \(14^{k+1}\)، لكنه ليس عددًا موجبًا حقيقيًا بطول \((k+1)\) رقمًا. ولهذا يحتفظ الكود بالمتغير leading_digit ولا يحسب الحالة لذلك الطول إلا إذا كان هذا الرقم غير صفري.

6) أمثلة صريحة للفروع. بدءًا من الجذرين غير التافهين نحصل على

$$7 \to 37 \to \mathrm{c37} \to 0\mathrm{c37} \to \mathrm{a0c37} \to \cdots$$

و

$$8 \to \mathrm{a8} \to 1\mathrm{a8} \to \mathrm{d1a8} \to 3\mathrm{d1a8} \to \cdots.$$

القيمة \(0\mathrm{c37}\) هي idempotent صحيحة modulo \(14^4\)، لكنها لا تُعد steady square بطول 4 لأن رقمها الأعلى الجديد يساوي \(0\). أما الفرع الخارج من \(1\) فيبقى دائمًا مع \(t=0\)، ولذا لا يساهم إلا بالعدد الأحادي \(1\).

7) تجميع مجموع الأرقام. إذا كان مجموع أرقام الفرع الحالي هو \(\sigma\) والرقم الأعلى الجديد هو \(t\)، فإن المجموع الجديد يساوي

$$\sigma'=\sigma+t.$$

لذلك لا يعيد البرنامج حساب مجموع الأرقام من البداية، بل يحتفظ بمجموع جارٍ لفرع \(7\) وآخر لفرع \(8\). ويبدأ المجموع الكلي من

$$1$$

كي يحسب steady square \(1\).

أمثلة عملية ونقاط تحقق

في الأطوال الأولى تكون steady square الموجبة كما يلي:

$$ n=1:\ 1,7,8;\qquad n=2:\ 37,\mathrm{a8};\qquad n=3:\ \mathrm{c37},1\mathrm{a8};\qquad n=4:\ \mathrm{d1a8}. $$

ولهذا تصبح المجاميع التراكمية لمجموع الأرقام

$$16,\ 44,\ 85,\ 117,\dots$$

ويتحقق الملف من أن

$$S(9)=582=2\mathrm{d}8_{14}.$$

كما يتحقق أيضًا خلال أول 20 خطوة رفع من أن كلا الفرعين ما زال يحقق \(x^2\equiv x\pmod{14^n}\).

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

هناك فرعان غير تافهين فقط، وفي كل زيادة للطول تُنفذ كمية ثابتة من الحسابات المعيارية وتحديثات الأعداد الكبيرة. لذلك يكون التعقيد الزمني

$$O(n_{\max}),$$

والذاكرة الإضافية

$$O(1).$$

القيم \(x\) نفسها تصبح ضخمة جدًا، ولهذا يستخدم الكود cpp_int، لكن فضاء الحالات لا يتشعب أبدًا.

مراجع إضافية

  1. صفحة المسألة: https://projecteuler.net/problem=284
  2. مبرهنة الباقي الصيني: https://ar.wikipedia.org/wiki/مبرهنة_الباقي_الصيني
  3. لمّة Hensel: https://en.wikipedia.org/wiki/Hensel%27s_lemma