For every prime \(p\neq2,5\), define the decimal divisibility multiplier \(m(p)\) as the number that lets us replace a divisibility test on
$$n=10q+r$$
by a test on the shorter number \(q+m(p)r\). The problem asks for the sum of these multipliers over all primes below a large limit. The final numeric answer is omitted here; what matters is the mathematical reason this multiplier exists and why the code computes it so quickly.
Any decimal integer can be split into quotient and last digit as
$$n=10q+r,\qquad 0\le r\le 9.$$
If \(p=2\) or \(p=5\), then \(10\equiv0\pmod p\), so \(10\) has no multiplicative inverse modulo \(p\). That means the whole “remove the last digit and adjust” trick cannot be expressed by multiplying with \(10^{-1}\).
For every other prime \(p\), we have \(\gcd(10,p)=1\), so a unique inverse of \(10\) exists modulo \(p\).
Start from the divisibility condition
$$p\mid n \iff p\mid (10q+r).$$
Let \(t\) be the modular inverse of \(10\), so
$$10t\equiv1\pmod p.$$
Multiply the congruence \(10q+r\equiv0\pmod p\) by \(t\):
$$q+tr\equiv0\pmod p.$$
Therefore
$$p\mid n \iff p\mid (q+tr).$$
So the divisibility multiplier is exactly
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
The code returns the least nonnegative representative of this inverse.
Sometimes schoolbook divisibility rules are written with subtraction instead of addition. That is the same thing, because if \(m(p)\) is the inverse of \(10\), then \(m(p)-p\) is an equivalent multiplier modulo \(p\).
For example, for \(p=7\) we have
$$10^{-1}\equiv5\pmod7,$$
so one valid rule is
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
But since \(5\equiv-2\pmod7\), this is the usual rule
$$7\mid(10q+r)\iff 7\mid(q-2r).$$
The Project Euler problem chooses the positive modular inverse.
Because \(10\cdot4=40\equiv1\pmod{13}\), we have
$$m(13)=4.$$
So
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
Apply that repeatedly to the divisible number \(3484\):
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
Since \(13\) is divisible by \(13\), the original number is also divisible by \(13\). This shows why the multiplier rule is a genuine digit-stripping test, not just a single-step congruence trick.
The code contains the checkpoint \(m(113)=34\). Here is why. Run the Euclidean algorithm:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
Now back-substitute:
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
Hence
$$10\cdot34\equiv1\pmod{113},$$
so
$$m(113)=34.$$
A quick check with \(791=7\cdot113\):
$$79+34\cdot1=113,$$
which is obviously divisible by \(113\), so the test works exactly as predicted.
Once the derivation is done, nothing deeper remains: for each prime \(p\neq2,5\), the desired multiplier is simply \(10^{-1}\pmod p\). So the problem becomes
$$\sum_{\substack{p<L\\ p\ \text{prime}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
with the inverse interpreted as its least nonnegative representative.
Prime generation. primes_up_to() uses a linear sieve to list all primes below
prime_limit.
Inverse computation. inverse_mod_10(p) runs an iterative extended Euclidean algorithm on
\(10\) and \(p\). The returned coefficient of \(10\) is reduced into the range \([0,p-1]\).
Accumulation. solve() skips \(2\) and \(5\), sums the inverses for all other primes, and
returns a 64-bit total.
Checkpoints. The source verifies
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
It also exposes --prime-limit=... and --skip-checkpoints for controlled runs.
With a linear sieve, generating primes up to \(L\) costs \(O(L)\) time and \(O(L)\) memory. For each prime, the extended Euclidean algorithm costs \(O(\log p)\), so the total post-sieve work is
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L).$$
For the given limit this is easily fast enough, because the arithmetic per prime is tiny.
Für jede Primzahl \(p\neq2,5\) wird ein Dezimal-Teilbarkeitsmultiplikator \(m(p)\) definiert. Er erlaubt, die Teilbarkeit einer Zahl
$$n=10q+r$$
durch eine kürzere Zahl \(q+m(p)r\) zu testen. Gesucht ist die Summe dieser Multiplikatoren für alle Primzahlen unter einer großen Schranke. Der Endwert ist hier nicht entscheidend; wichtig ist die Herleitung des Multiplikators und warum der Code ihn so direkt berechnet.
Jede Dezimalzahl lässt sich als
$$n=10q+r,\qquad 0\le r\le 9$$
schreiben. Für \(p=2\) oder \(p=5\) gilt jedoch \(10\equiv0\pmod p\); also besitzt \(10\) kein multiplikatives Inverses modulo \(p\). Genau deshalb funktioniert die „letzte Ziffer abspalten und korrigieren“-Regel dort nicht in dieser Form.
Für jede andere Primzahl ist \(\gcd(10,p)=1\), also existiert \(10^{-1}\pmod p\).
Wir beginnen mit
$$p\mid n \iff p\mid(10q+r).$$
Sei \(t\) das Inverse von \(10\) modulo \(p\), also
$$10t\equiv1\pmod p.$$
Multipliziert man die Kongruenz \(10q+r\equiv0\pmod p\) mit \(t\), so erhält man
$$q+tr\equiv0\pmod p.$$
Damit folgt
$$p\mid n \iff p\mid(q+tr).$$
Der gesuchte Teilbarkeitsmultiplikator ist also exakt
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
Der Code liefert den kleinsten nichtnegativen Repräsentanten dieses Inversen.
Manche Teilbarkeitsregeln werden mit einem Minuszeichen formuliert. Das ist derselbe Inhalt, denn zu \(m(p)\) ist auch \(m(p)-p\) modulo \(p\) äquivalent.
Für \(p=7\) gilt zum Beispiel
$$10^{-1}\equiv5\pmod7,$$
also
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
Da aber \(5\equiv-2\pmod7\), ist das dieselbe Regel wie
$$7\mid(10q+r)\iff 7\mid(q-2r).$$
Das Euler-Problem verwendet die positive Inverse.
Da \(10\cdot4=40\equiv1\pmod{13}\), gilt
$$m(13)=4.$$
Also
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
Auf die durch \(13\) teilbare Zahl \(3484\) angewandt:
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
Da \(13\) durch \(13\) teilbar ist, war auch \(3484\) durch \(13\) teilbar. So sieht man, dass die Regel wiederholt angewandt wirklich die Zahl Stelle für Stelle verkürzt.
Der Code überprüft die Kontrollbedingung \(m(113)=34\). Herleitung mit Euklid:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
Rückwärtseinsetzen liefert
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
Daher
$$10\cdot34\equiv1\pmod{113},$$
also
$$m(113)=34.$$
Kurzer Test mit \(791=7\cdot113\):
$$79+34\cdot1=113,$$
also bestätigt auch die Regel sofort die Teilbarkeit durch \(113\).
Nach der Herleitung bleibt nichts weiter übrig: Für jede Primzahl \(p\neq2,5\) ist der gesuchte Multiplikator einfach \(10^{-1}\pmod p\). Das Problem reduziert sich also auf
$$\sum_{\substack{p<L\\ p\ \text{prim}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
wobei stets der kleinste nichtnegative Rest gemeint ist.
Primzahlerzeugung. primes_up_to() verwendet ein lineares Sieb und erzeugt alle
Primzahlen unter prime_limit.
Inverse berechnen. inverse_mod_10(p) führt den erweiterten euklidischen Algorithmus für
\(10\) und \(p\) iterativ aus. Der Koeffizient von \(10\) wird anschließend in den Bereich \([0,p-1]\) reduziert.
Aufsummieren. solve() überspringt \(2\) und \(5\), addiert für alle übrigen Primzahlen
die Inversen und gibt die 64-Bit-Summe zurück.
Checkpoints. Die Quelle testet
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
Zusätzlich gibt es die Optionen --prime-limit=... und --skip-checkpoints.
Mit linearem Sieb kostet die Primzahlerzeugung bis \(L\) Zeit \(O(L)\) und Speicher \(O(L)\). Für jede Primzahl benötigt der erweiterte Euklid \(O(\log p)\), sodass der Zusatzaufwand nach dem Sieb
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L)$$
beträgt. Für die gegebene Grenze ist das mehr als schnell genug.
Her \(p\neq2,5\) asalı için onluk bölünebilme çarpanı \(m(p)\) tanımlanır. Bu çarpan sayesinde
$$n=10q+r$$
biçimindeki bir sayının \(p\)'ye bölünebilirliği, daha kısa olan \(q+m(p)r\) sayısına indirgenebilir. Problem, büyük bir üst sınırın altındaki tüm asallar için bu çarpanları toplamayı ister. Burada önemli olan nihai cevap değil, çarpanın neden var olduğu ve kodun bunu neden çok hızlı hesaplayabildiğidir.
Her onluk sayı
$$n=10q+r,\qquad 0\le r\le 9$$
şeklinde yazılabilir. Eğer \(p=2\) veya \(p=5\) ise \(10\equiv0\pmod p\) olur; yani 10'un mod \(p\) çarpımsal tersi yoktur. Son basamağı atıp düzeltme yapan testin temel dayanağı bu ters olduğu için, aynı yaklaşım bu iki asal için çalışmaz.
Diğer her asal için \(\gcd(10,p)=1\) olduğundan \(10^{-1}\pmod p\) vardır.
Bölünebilme koşulundan başlayalım:
$$p\mid n \iff p\mid(10q+r).$$
\(t\), 10'un mod \(p\) tersi olsun:
$$10t\equiv1\pmod p.$$
\(10q+r\equiv0\pmod p\) denkliğini \(t\) ile çarparsak
$$q+tr\equiv0\pmod p$$
elde ederiz. Dolayısıyla
$$p\mid n \iff p\mid(q+tr).$$
Yani aranan bölünebilme çarpanı tam olarak
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
Kod bu tersin en küçük negatif olmayan temsilcisini döndürür.
Bazı okul tipi bölünebilme kuralları toplama yerine çıkarma ile yazılır. Bu aslında aynı şeydir; çünkü \(m(p)\) yerine \(m(p)-p\) de aynı modüler çarpandır.
Örneğin \(p=7\) için
$$10^{-1}\equiv5\pmod7,$$
dolayısıyla geçerli bir kural
$$7\mid(10q+r)\iff 7\mid(q+5r)$$
olur. Ama \(5\equiv-2\pmod7\) olduğundan bu bildiğimiz
$$7\mid(10q+r)\iff 7\mid(q-2r)$$
kuralıyla aynıdır. Project Euler sorusu pozitif modüler tersi kullanır.
\(10\cdot4=40\equiv1\pmod{13}\) olduğundan
$$m(13)=4.$$
Yani
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
Bunu \(13\)'e bölünebilen \(3484\) sayısına tekrar tekrar uygulayalım:
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
Sonunda \(13\) kaldığı için ilk sayı da \(13\)'e bölünür. Bu örnek, kuralın sadece tek adımlık bir kongruans hilesi değil, gerçekten sağdan sola basamak küçülten bir test olduğunu gösterir.
Kod, \(m(113)=34\) kontrolünü yapar. Sebebi şudur:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
Geri yerine koyarsak
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
Buradan
$$10\cdot34\equiv1\pmod{113}$$
çıkar; yani
$$m(113)=34.$$
Kısa bir kontrol için \(791=7\cdot113\) alınırsa
$$79+34\cdot1=113$$
elde edilir ve sonuç doğrudan \(113\)'e bölünür.
Bu türetim yapıldıktan sonra geriye daha derin bir yapı kalmaz: her \(p\neq2,5\) asalı için istenen çarpan yalnızca \(10^{-1}\pmod p\) değeridir. Dolayısıyla problem
$$\sum_{\substack{p<L\\ p\ \text{asal}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p$$
toplamını hesaplama işine dönüşür; burada ters, en küçük negatif olmayan temsilci olarak alınır.
Asal üretimi. primes_up_to(), prime_limit altındaki tüm asalları lineer elek
ile üretir.
Ters hesabı. inverse_mod_10(p), 10 ile \(p\) üzerinde iteratif genişletilmiş Öklid
çalıştırır. 10'un katsayısı en sonda \([0,p-1]\) aralığına normalize edilir.
Toplama. solve(), \(2\) ve \(5\)'i atlar, diğer asallar için bulunan tersleri toplayıp
64 bitlik sonucu döndürür.
Checkpoint'ler. Kaynak kod şu iki kontrolü yapar:
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
Ayrıca --prime-limit=... ve --skip-checkpoints seçenekleri vardır.
Lineer elek ile \(L\)'ye kadar asal üretmek \(O(L)\) zaman ve \(O(L)\) bellek gerektirir. Her asal için genişletilmiş Öklid \(O(\log p)\) sürer; bu yüzden elekten sonraki toplam iş
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L)$$
olur. Verilen sınır için bu rahatlıkla yeterlidir.
Para cada primo \(p\neq2,5\), se define un multiplicador decimal de divisibilidad \(m(p)\). Ese número permite sustituir la prueba de divisibilidad de
$$n=10q+r$$
por la de un número más corto, \(q+m(p)r\). El problema pide sumar dichos multiplicadores para todos los primos por debajo de un gran límite. El valor final no es lo importante aquí; lo esencial es entender por qué ese multiplicador existe y por qué el código se reduce a calcular inversos modulares.
Cualquier entero decimal se escribe como
$$n=10q+r,\qquad 0\le r\le 9.$$
Si \(p=2\) o \(p=5\), entonces \(10\equiv0\pmod p\), de modo que \(10\) no tiene inverso modular. Por eso la regla de “quitar la última cifra y corregir” no puede formularse igual para esos dos primos.
Para cualquier otro primo, \(\gcd(10,p)=1\), así que existe \(10^{-1}\pmod p\).
Partimos de
$$p\mid n \iff p\mid(10q+r).$$
Sea \(t\) el inverso modular de \(10\):
$$10t\equiv1\pmod p.$$
Multiplicando \(10q+r\equiv0\pmod p\) por \(t\), obtenemos
$$q+tr\equiv0\pmod p.$$
Por tanto,
$$p\mid n \iff p\mid(q+tr).$$
Luego el multiplicador buscado es exactamente
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
El programa devuelve el representante no negativo más pequeño.
Muchas reglas escolares usan resta en lugar de suma. Eso es equivalente, porque \(m(p)-p\) representa la misma clase modular que \(m(p)\).
Para \(p=7\), por ejemplo,
$$10^{-1}\equiv5\pmod7,$$
así que una regla válida es
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
Como \(5\equiv-2\pmod7\), eso coincide con la regla habitual
$$7\mid(10q+r)\iff 7\mid(q-2r).$$
La formulación del problema usa el inverso positivo.
Puesto que \(10\cdot4=40\equiv1\pmod{13}\), tenemos
$$m(13)=4.$$
Entonces
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
Apliquemos la regla repetidamente a \(3484\):
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
Como \(13\) es divisible por \(13\), el número original también lo es. Esto muestra que la regla realmente elimina cifras desde la derecha.
El código verifica que \(m(113)=34\). La razón es
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
Retrocediendo,
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
Por ello
$$10\cdot34\equiv1\pmod{113},$$
y así
$$m(113)=34.$$
Comprobación rápida con \(791=7\cdot113\):
$$79+34\cdot1=113,$$
que claramente es múltiplo de \(113\).
Una vez hecha la derivación, ya no queda ninguna estructura oculta: para cada primo \(p\neq2,5\), el multiplicador pedido es simplemente \(10^{-1}\pmod p\). Por tanto, el problema se convierte en calcular
$$\sum_{\substack{p<L\\ p\ \text{primo}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
tomando siempre el representante no negativo más pequeño.
Generación de primos. primes_up_to() usa una criba lineal para enumerar todos los primos
por debajo de prime_limit.
Cálculo del inverso. inverse_mod_10(p) aplica el algoritmo extendido de Euclides a \(10\)
y \(p\), y luego normaliza el coeficiente de \(10\) al intervalo \([0,p-1]\).
Acumulación. solve() omite \(2\) y \(5\), suma los inversos de los demás primos y devuelve
el total en 64 bits.
Puntos de control. El código comprueba
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
También admite --prime-limit=... y --skip-checkpoints.
Con una criba lineal, generar los primos hasta \(L\) cuesta \(O(L)\) tiempo y \(O(L)\) memoria. Para cada primo, el algoritmo extendido de Euclides cuesta \(O(\log p)\), así que el trabajo adicional es
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L).$$
Eso es muy eficiente para el límite del problema.
对每个素数 \(p\neq2,5\),定义十进制整除乘子 \(m(p)\)。它的作用是把
$$n=10q+r$$
的整除判定,改写成更短数字 \(q+m(p)r\) 的整除判定。题目要求把某个大上界以下所有素数对应的乘子求和。这里不强调最 终数值,而是说明这个乘子为什么存在,以及代码为什么只需要求模逆元。
任意十进制整数都可写成
$$n=10q+r,\qquad 0\le r\le 9.$$
如果 \(p=2\) 或 \(p=5\),那么 \(10\equiv0\pmod p\),所以 \(10\) 在模 \(p\) 下没有乘法逆元。因此“去掉最后一位再修正” 这类规则不能用同样的逆元推导来表达。
对其他任意素数,都有 \(\gcd(10,p)=1\),于是 \(10^{-1}\pmod p\) 存在。
从整除条件开始:
$$p\mid n \iff p\mid(10q+r).$$
设 \(t\) 是 \(10\) 在模 \(p\) 下的逆元,即
$$10t\equiv1\pmod p.$$
把 \(10q+r\equiv0\pmod p\) 两边乘上 \(t\),得到
$$q+tr\equiv0\pmod p.$$
因此
$$p\mid n \iff p\mid(q+tr).$$
所以所求乘子正是
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
代码返回的是这个逆元在 \([0,p-1]\) 中的最小非负代表。
很多学校里的整除规则写成减法形式,其实完全等价,因为 \(m(p)-p\) 与 \(m(p)\) 表示同一个模类。
例如对 \(p=7\),有
$$10^{-1}\equiv5\pmod7,$$
因此可以写成
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
但由于 \(5\equiv-2\pmod7\),这与熟悉的规则
$$7\mid(10q+r)\iff 7\mid(q-2r)$$
完全一样。Project Euler 这里采用正的模逆。
因为 \(10\cdot4=40\equiv1\pmod{13}\),所以
$$m(13)=4.$$
于是
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
把它反复应用到 \(3484\):
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
最后得到 \(13\),显然可被 \(13\) 整除,所以原数也可被 \(13\) 整除。这说明该乘子规则确实可以不断剥去最后一位数字。
代码中的检查点之一是 \(m(113)=34\)。推导如下:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
回代可得
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
因此
$$10\cdot34\equiv1\pmod{113},$$
从而
$$m(113)=34.$$
再看 \(791=7\cdot113\):
$$79+34\cdot1=113,$$
立刻得到一个明显可被 \(113\) 整除的数。
推导完成后,问题已经没有别的隐藏结构:对每个 \(p\neq2,5\),所求乘子就是 \(10^{-1}\pmod p\)。因此目标等价于
$$\sum_{\substack{p<L\\ p\ \text{是素数}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
其中逆元始终取最小非负代表。
素数筛。 primes_up_to() 用线性筛生成所有小于 prime_limit 的素数。
逆元计算。 inverse_mod_10(p) 对 \(10\) 和 \(p\) 运行迭代版扩展欧几里得算法,并把得到的
\(10\) 的系数归一化到 \([0,p-1]\)。
累加。 solve() 跳过 \(2\) 和 \(5\),对其余所有素数求逆元并累加到 64 位答案中。
检查点。 源码验证
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
同时支持 --prime-limit=... 和 --skip-checkpoints。
线性筛生成到 \(L\) 的素数需要 \(O(L)\) 时间和 \(O(L)\) 内存。对每个素数,扩展欧几里得算法耗时 \(O(\log p)\),因此筛后额外工作量为
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L).$$
对题目给定的规模,这个复杂度完全足够。
Для каждого простого \(p\neq2,5\) вводится десятичный множитель делимости \(m(p)\). С его помощью проверку делимости числа
$$n=10q+r$$
можно заменить проверкой более короткого числа \(q+m(p)r\). Задача просит просуммировать такие множители по всем простым ниже большого предела. Важно не столько само итоговое число, сколько понимание того, почему множитель существует и почему код фактически сводится к вычислению обратного элемента к \(10\) по модулю \(p\).
Любое десятичное число представимо в виде
$$n=10q+r,\qquad 0\le r\le 9.$$
Если \(p=2\) или \(p=5\), то \(10\equiv0\pmod p\), а значит, у числа \(10\) нет обратного по модулю \(p\). Поэтому стандартный приём «отбросить последнюю цифру и скорректировать остаток» нельзя вывести тем же способом.
Для любой другой простой \(p\) имеем \(\gcd(10,p)=1\), поэтому \(10^{-1}\pmod p\) существует.
Начинаем с условия делимости:
$$p\mid n \iff p\mid(10q+r).$$
Пусть \(t\) — обратный элемент к \(10\) по модулю \(p\), то есть
$$10t\equiv1\pmod p.$$
Умножая сравнение \(10q+r\equiv0\pmod p\) на \(t\), получаем
$$q+tr\equiv0\pmod p.$$
Следовательно,
$$p\mid n \iff p\mid(q+tr).$$
Значит, искомый множитель равен
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
Код берёт наименьший неотрицательный представитель этого класса.
Во многих правилах делимости фигурирует вычитание, а не сложение. Это эквивалентно, потому что \(m(p)-p\) задаёт тот же остаток, что и \(m(p)\).
Например, для \(p=7\)
$$10^{-1}\equiv5\pmod7,$$
поэтому корректное правило имеет вид
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
Но \(5\equiv-2\pmod7\), так что это та же самая привычная проверка
$$7\mid(10q+r)\iff 7\mid(q-2r).$$
В задаче используется положительный модульный обратный.
Поскольку \(10\cdot4=40\equiv1\pmod{13}\), имеем
$$m(13)=4.$$
Поэтому
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
Применим правило несколько раз к числу \(3484\):
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
Так как \(13\) делится на \(13\), исходное число тоже делится на \(13\). Это показывает, что правило действительно позволяет укорачивать число справа налево.
В коде есть проверка \(m(113)=34\). Она выводится так:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
Делаем обратную подстановку:
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
Значит,
$$10\cdot34\equiv1\pmod{113},$$
то есть
$$m(113)=34.$$
Быстрая проверка на \(791=7\cdot113\):
$$79+34\cdot1=113,$$
и это число уже очевидно делится на \(113\).
После вывода формулы больше ничего скрытого не остаётся: для каждого простого \(p\neq2,5\) искомый множитель — это просто \(10^{-1}\pmod p\). Следовательно, задача сводится к вычислению суммы
$$\sum_{\substack{p<L\\ p\ \text{простое}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
где всегда берётся наименьший неотрицательный представитель.
Генерация простых. primes_up_to() использует линейное решето и строит все простые
меньше prime_limit.
Вычисление обратного. inverse_mod_10(p) запускает итеративный расширенный алгоритм
Евклида для \(10\) и \(p\), после чего нормализует коэффициент при \(10\) в диапазон \([0,p-1]\).
Накопление суммы. solve() пропускает \(2\) и \(5\), суммирует обратные для остальных
простых и возвращает 64-битный результат.
Контрольные точки. Исходник проверяет
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
Также поддерживаются параметры --prime-limit=... и --skip-checkpoints.
Линейное решето до \(L\) требует \(O(L)\) времени и \(O(L)\) памяти. Для каждого простого расширенный Евклид работает за \(O(\log p)\), поэтому дополнительная работа после решета равна
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L).$$
Для предела задачи этого более чем достаточно.
لكل عدد أولي \(p\neq2,5\) نعرّف مضاعِف القابلية للقسمة العشري \(m(p)\). هذا العدد يسمح بتحويل اختبار القسمة للعدد
$$n=10q+r$$
إلى اختبار على عدد أقصر هو \(q+m(p)r\). المطلوب هو جمع هذه المضاعفات لكل الأوليات الأصغر من حد كبير. المقصود هنا ليس عرض الناتج النهائي، بل توضيح سبب وجود هذا المضاعف ولماذا تنتهي الشيفرة إلى حساب معكوس \(10\) معيارياً فقط.
كل عدد عشري يمكن كتابته على الصورة
$$n=10q+r,\qquad 0\le r\le 9.$$
إذا كان \(p=2\) أو \(p=5\)، فإن \(10\equiv0\pmod p\)، وبالتالي لا يملك \(10\) معكوساً ضربياً modulo \(p\). لهذا لا يمكن اشتقاق قاعدة “احذف آخر رقم ثم صحّح” بالطريقة نفسها لهذين الأوليين.
أما لأي أولي آخر فلدينا \(\gcd(10,p)=1\)، ولذلك يوجد \(10^{-1}\pmod p\).
نبدأ من شرط القسمة:
$$p\mid n \iff p\mid(10q+r).$$
ليكن \(t\) هو معكوس \(10\) modulo \(p\)، أي
$$10t\equiv1\pmod p.$$
إذا ضربنا العلاقة \(10q+r\equiv0\pmod p\) في \(t\)، نحصل على
$$q+tr\equiv0\pmod p.$$
ومن ثم
$$p\mid n \iff p\mid(q+tr).$$
إذن المضاعف المطلوب هو بالضبط
$$\boxed{m(p)\equiv10^{-1}\pmod p.}$$
وتعيد الشيفرة أصغر ممثل غير سالب لهذا المعكوس.
كثير من قواعد القسمة المدرسية تُكتب بصيغة الطرح بدلاً من الجمع. هذا مكافئ تماماً لأن \(m(p)-p\) يمثل نفس الفئة المعيارية التي يمثلها \(m(p)\).
فمثلاً عندما \(p=7\) يكون
$$10^{-1}\equiv5\pmod7,$$
ومن ثم قاعدة صحيحة هي
$$7\mid(10q+r)\iff 7\mid(q+5r).$$
لكن بما أن \(5\equiv-2\pmod7\)، فهذا هو نفسه القانون الشائع
$$7\mid(10q+r)\iff 7\mid(q-2r).$$
مسألة Euler تختار المعكوس الموجب.
بما أن \(10\cdot4=40\equiv1\pmod{13}\)، فإن
$$m(13)=4.$$
وعليه
$$13\mid(10q+r)\iff 13\mid(q+4r).$$
نطبق هذا مراراً على العدد \(3484\):
$$3484 \longrightarrow 348+4\cdot4=364,$$
$$364 \longrightarrow 36+4\cdot4=52,$$
$$52 \longrightarrow 5+4\cdot2=13.$$
وبما أن \(13\) يقبل القسمة على \(13\)، فإن العدد الأصلي أيضاً يقبلها. وهذا يبيّن أن القاعدة ليست خدعة توافق خطوة واحدة فقط، بل وسيلة حقيقية لتقصير العدد من اليمين إلى اليسار.
تتحقق الشيفرة من أن \(m(113)=34\). والسبب هو:
$$113=11\cdot10+3,\qquad 10=3\cdot3+1.$$
وبالرجوع العكسي نحصل على
$$1=10-3\cdot3=10-(113-11\cdot10)\cdot3=34\cdot10-3\cdot113.$$
إذن
$$10\cdot34\equiv1\pmod{113},$$
ومن ثم
$$m(113)=34.$$
وفحص سريع مع \(791=7\cdot113\):
$$79+34\cdot1=113,$$
وهو عدد يقبل القسمة على \(113\) مباشرة.
بعد هذا الاشتقاق لا يبقى شيء خفي: لكل أولي \(p\neq2,5\)، المضاعف المطلوب هو فقط \(10^{-1}\pmod p\). لذلك تصبح المسألة حساب المجموع
$$\sum_{\substack{p<L\\ p\ \text{أولي}\\ p\neq2,5}} 10^{-1}\!\!\!\pmod p,$$
مع أخذ أصغر ممثل غير سالب دائماً.
توليد الأوليات. تستعمل primes_up_to() غربالاً خطياً لتوليد جميع الأوليات الأصغر من
prime_limit.
حساب المعكوس. تنفذ inverse_mod_10(p) خوارزمية إقليدس الممتدة بشكل تكراري على \(10\) و\(p\)، ثم
تطبع معامل \(10\) إلى المجال \([0,p-1]\).
التجميع. تتجاوز solve() العددين \(2\) و\(5\)، وتجمع معكوسات بقية الأوليات في مجموع
64-بت.
نقاط التحقق. يتحقق المصدر من
$$m(113)=34,\qquad \text{solve}(1000)=39517.$$
كما يدعم الوسيطتين --prime-limit=... و--skip-checkpoints.
باستخدام الغربال الخطي، يكلف توليد الأوليات حتى \(L\) زمناً \(O(L)\) وذاكرة \(O(L)\). ولكل أولي، تكلف خوارزمية إقليدس الممتدة \(O(\log p)\)، لذا فإن العمل الإضافي بعد الغربال يساوي
$$O\!\left(\sum_{p<L}\log p\right)=O(\pi(L)\log L).$$
وهذا أكثر من كافٍ لحد المسألة.