The automaton in Problem 308 is a graphical encoding of Conway's prime-generating FRACTRAN program. If we simulate it literally, the number of steps explodes long before we reach the \(10001\)-st prime output. The key is to understand one complete candidate cycle analytically and count steps without walking through every transition.
The FRACTRAN machine can be represented by exponent counts of the primes
$$2,3,5,7,11,13,17,19,23,29.$$
So a machine state is equivalent to a vector of exponents, or to the integer
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
The automaton “prints” a value exactly when the state is a pure power of two:
$$N=2^m.$$
Then the printed number is the exponent \(m\). The brute-force checkpoint in the C++ code confirms that the first outputs are
$$2,3,5,7,11,13,17,19,23,29,\dots$$
with step indices
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
The program does not test primes by trial division in the usual sense. Instead, the FRACTRAN dynamics repeatedly reaches a canonical state that encodes “we are now testing candidate \(n\).” In the optimized derivation, the first step index of that canonical state is denoted by
$$T_n.$$
The source code uses
$$T_2=2,$$
meaning that after two initial transitions from the seed, the machine has entered the standard start configuration for candidate \(2\).
Once candidate \(n\) starts, the automaton behaves like a deterministic divisor-testing routine.
Setup: it builds the control structure for the current candidate.
Middle phase: it tests divisors \(d\) in descending order.
Exit: if a divisor is found, the machine moves on to candidate \(n+1\); if no divisor exists, it follows a special prime branch and emits \(2^n\).
This is why the whole process can be summarized by two exact arithmetic quantities:
$$\Delta(n)=T_{n+1}-T_n,$$
the total step count from the start of candidate \(n\) to the start of candidate \(n+1\), and
$$H(p),$$
the offset from \(T_p\) to the moment where prime candidate \(p\) is actually emitted.
Let
$$s=\operatorname{spf}(n)$$
be the smallest prime factor of \(n\). Then the first divisor encountered while scanning downward is the largest proper divisor
$$d_*=\frac{n}{s}.$$
All integers
$$d=n-1,n-2,\dots,d_*+1$$
are guaranteed to be non-divisors, and the scan stops exactly at \(d_*\). That is why the composite-case formula naturally splits into “all non-divisor tests above \(d_*\)” plus “the final successful divisor branch at \(d_*\).”
The code has already flattened the entire state graph of one candidate cycle into exact transition counts.
For a general candidate \(n\), the total jump to the next candidate is
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
The three parts are:
Setup: \(2n-1\).
Rejected divisor tests: one term \(6n+2+2\lfloor n/d\rfloor\) for every non-divisor above \(d_*\).
Terminal composite branch: the final successful branch at \(d_*\).
If \(p\) is prime, there is no divisor in \(2,\dots,p-1\), so the machine instead reaches the prime-output branch:
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
The last term \(6p+2\) is the prime branch itself.
Once \(\Delta(n)\) is known, the candidate-start times satisfy the prefix recurrence
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
If \(p_k\) is the \(k\)-th prime, then the answer to the Project Euler problem is simply
$$T_{p_k}+H(p_k).$$
For example, candidate \(2\) starts at step \(T_2=2\), and
$$H(2)=17,$$
so the first prime hit occurs at
$$2+17=19.$$
Likewise, \(\Delta(2)=20\), so \(T_3=22\); then \(H(3)=47\), giving the second prime hit at
$$22+47=69.$$
These match the brute-force checkpoints exactly.
The expensive-looking part is
$$\sum \lfloor n/d\rfloor.$$
The implementation evaluates such sums in quotient blocks: if several consecutive divisors have the same quotient \(q=\lfloor n/d\rfloor\), they are added at once. This is the standard harmonic-grouping trick and makes each floor-sum sublinear instead of linear.
1) Prime bound. The solver first estimates an upper bound for the \(k\)-th prime using \(n(\log n+\log\log n)\)-type asymptotics, then enlarges it if necessary.
2) Linear sieve. A sieve produces both the prime list and the smallest-prime-factor table \(\operatorname{spf}(n)\).
3) Floor-sum helpers. floor_sum_upto() and floor_sum_range() implement quotient-block summation.
4) Closed formulas. candidate_delta() computes \(\Delta(n)\), and prime_hit_offset() computes \(H(p)\).
5) Prefix walk. The main loop advances \(T_n\) candidate by candidate until it reaches the target prime \(p_k\), then returns \(T_{p_k}+H(p_k)\).
6) Strong validation. The C++ code also contains a literal automaton simulator and checks that the first 10 prime hits and several small target indices agree with the closed formulas.
The sieve costs \(O(B)\) time and memory up to the chosen prime bound \(B\). Each candidate then needs only a constant amount of algebra plus a few harmonic floor-sums, each taking about \(O(\sqrt n)\) block steps. This is enormously faster than literal FRACTRAN simulation, which would need to traverse about \(1.5\times10^{15}\) transitions for the final answer.
Der Automat aus Problem 308 ist eine graphische Codierung von Conways primzahl-erzeugendem FRACTRAN-Programm. Eine wörtliche Schritt-für-Schritt-Simulation wächst viel zu schnell an. Entscheidend ist daher, einen vollständigen Kandidatenzyklus analytisch zu verstehen und die Schrittzahlen direkt zu berechnen.
Ein Zustand kann durch die Exponenten der Primzahlen
$$2,3,5,7,11,13,17,19,23,29$$
beschrieben werden, also durch
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
Ein „Output“ entsteht genau dann, wenn der Zustand eine reine Zweierpotenz ist:
$$N=2^m.$$
Dann ist die ausgegebene Zahl der Exponent \(m\). Der brute-force-Checkpoint im C++-Code bestätigt die ersten Ausgaben
$$2,3,5,7,11,13,17,19,23,29,\dots$$
an den Schritten
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
Die Dynamik erreicht immer wieder einen kanonischen Zustand, der bedeutet: „Jetzt wird Kandidat \(n\) getestet.“ Der erste Schrittindex dieses Zustands heißt
$$T_n.$$
Im Quellcode wird
$$T_2=2$$
gesetzt, weil nach zwei Anfangsübergängen der Standard-Startzustand für Kandidat \(2\) erreicht ist.
Ein vollständiger Zyklus für Kandidat \(n\) verhält sich wie ein deterministischer Teilbarkeitstest:
Setup: Der Kontrollzustand für \(n\) wird aufgebaut.
Mittelteil: Mögliche Teiler \(d\) werden absteigend getestet.
Ende: Bei einem Teiler geht es zu Kandidat \(n+1\); ohne Teiler wird ein spezieller Primzweig durchlaufen und \(2^n\) ausgegeben.
Darum genügen zwei exakte Größen:
$$\Delta(n)=T_{n+1}-T_n$$
und, für Primzahlen \(p\), der Treffer-Offset
$$H(p).$$
Sei
$$s=\operatorname{spf}(n)$$
der kleinste Primfaktor von \(n\). Dann ist der erste Teiler, den die absteigende Suche trifft, der größte echte Teiler
$$d_*=\frac{n}{s}.$$
Alle Werte
$$d=n-1,n-2,\dots,d_*+1$$
sind sicher Nichtteiler. Genau deshalb zerfällt die Formel in „alle erfolglosen Tests oberhalb von \(d_*\)“ plus „den letzten erfolgreichen Zweig bei \(d_*\)“.
Der Code hat den gesamten Zustandsgraphen eines Zyklus bereits zu exakten Summen zusammengezogen.
Für einen allgemeinen Kandidaten \(n\) gilt
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
Die drei Bestandteile sind Setup, alle fehlgeschlagenen Teilerprüfungen und der abschließende Composite-Zweig.
Für eine Primzahl \(p\) gibt es keinen Teiler in \(2,\dots,p-1\), also
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
Der letzte Summand ist hier der eigentliche Primzweig.
Mit \(\Delta(n)\) folgt rekursiv
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
Ist \(p_k\) die \(k\)-te Primzahl, dann lautet die gesuchte Antwort
$$T_{p_k}+H(p_k).$$
Beispiel: \(H(2)=17\), also liegt der erste Prime-Hit bei
$$2+17=19.$$
Außerdem ist \(\Delta(2)=20\), also \(T_3=22\), und mit \(H(3)=47\) erhält man
$$22+47=69.$$
Das stimmt exakt mit der brutalen Simulation überein.
Die teuren Terme sehen aus wie
$$\sum \lfloor n/d\rfloor.$$
Sie werden im Code in Quotientenblöcken ausgewertet: Wenn viele aufeinanderfolgende \(d\) denselben Quotienten liefern, werden sie gemeinsam summiert. Das ist der übliche harmonische Blocktrick.
1) Primgrenze. Zuerst wird per \(n(\log n+\log\log n)\)-Abschätzung eine Schranke für die \(k\)-te Primzahl gewählt und bei Bedarf verdoppelt.
2) Lineares Sieb. Das Sieb liefert sowohl die Primliste als auch \(\operatorname{spf}(n)\).
3) Floor-Sum-Helfer. floor_sum_upto() und floor_sum_range() implementieren die Quotientenblock-Idee.
4) Geschlossene Formeln. candidate_delta() berechnet \(\Delta(n)\), prime_hit_offset() berechnet \(H(p)\).
5) Präfixdurchlauf. Die Hauptschleife läuft Kandidat für Kandidat bis \(p_k\) und gibt dann \(T_{p_k}+H(p_k)\) zurück.
6) Harte Verifikation. Zusätzlich enthält die C++-Lösung einen wörtlichen Automaten-Simulator und vergleicht die ersten 10 Treffer sowie mehrere kleine Zielindizes mit der geschlossenen Formel.
Das Sieb kostet \(O(B)\) Zeit und Speicher bis zur gewählten Primgrenze \(B\). Jeder Kandidat braucht danach nur wenige harmonische Floor-Summen mit etwa \(O(\sqrt n)\) Blockschritten. Das ist um Größenordnungen schneller als die direkte FRACTRAN-Simulation, die für die Endantwort etwa \(1.5\times10^{15}\) Übergänge benötigen würde.
Problem 308'deki otomaton, Conway'nin asal üreten FRACTRAN programının grafiksel bir kodlamasıdır. Ham biçimde adım adım simüle etmek, \(10001\). asal çıktıya ulaşmadan çok önce patlar. Bu yüzden doğru yaklaşım, bir tam aday döngüsünü analitik olarak çözmek ve adım sayılarını doğrudan toplamaktır.
Makinenin durumu şu asalların üslerinden oluşan bir vektör olarak görülebilir:
$$2,3,5,7,11,13,17,19,23,29.$$
Yani durum eşdeğer olarak
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j$$
sayısıdır. Otomaton yalnızca durum saf bir iki kuvveti olduğunda “çıktı verir”:
$$N=2^m.$$
Bu durumda basılan değer üs \(m\)'dir. C++ içindeki brute-force doğrulaması ilk çıktıların
$$2,3,5,7,11,13,17,19,23,29,\dots$$
ve karşılık gelen adımların
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
olduğunu doğrular.
Makine sürekli olarak “şimdi \(n\) adayı test ediliyor” anlamına gelen kanonik bir başlangıç durumuna geri döner. Bu durumun ilk görüldüğü adım
$$T_n$$
ile gösterilir. Kaynak kod
$$T_2=2$$
kullanır; yani başlangıç çekirdeğinden iki geçiş sonra aday \(2\) için standart test durumuna girilmiştir.
Bir aday \(n\) için tam döngü, aslında deterministik bir bölen testi gibi davranır:
Kurulum: \(n\) için kontrol yapısı hazırlanır.
Orta faz: olası bölenler \(d\) büyükten küçüğe sınanır.
Çıkış: bölen bulunursa aday \(n+1\)'e geçilir; hiç bölen yoksa özel asal dalı çalışır ve \(2^n\) çıktısı üretilir.
Bu yüzden tüm döngü iki nicelikle özetlenebilir:
$$\Delta(n)=T_{n+1}-T_n$$
ve asal aday için vurma ofseti
$$H(p).$$
\(n\)'nin en küçük asal böleni
$$s=\operatorname{spf}(n)$$
olsun. O zaman büyükten küçüğe taramada ilk görülen bölen, en büyük uygun bölen olur:
$$d_*=\frac{n}{s}.$$
Böylece
$$d=n-1,n-2,\dots,d_*+1$$
aralığındaki tüm değerler kesin olarak bölen değildir ve tarama tam \(d_*\) noktasında durur. Bu, composite durum formülünün neden “önce başarısız testler, sonra son başarılı dal” biçiminde ayrıldığını açıklar.
Kod, tek bir aday döngüsünün bütün durum grafiğini tam kapalı forma indirmiştir.
Genel \(n\) için bir sonraki aday başlangıcına geçiş:
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
Burada üç parça sırasıyla kurulum, başarısız bölen denemeleri ve son composite dalıdır.
Eğer \(p\) asal ise \(2,\dots,p-1\) arasında bölen yoktur; bu yüzden prime-hit ofseti
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2)$$
olur. Son \(6p+2\) terimi asal çıktı dalının kendisidir.
\(\Delta(n)\) bilindiğinde aday başlangıç zamanları
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2$$
ile bulunur. Eğer \(p_k\), \(k\). asal ise cevap
$$T_{p_k}+H(p_k)$$
şeklindedir. Örneğin ilk asal vurum için \(H(2)=17\), dolayısıyla
$$2+17=19$$
elde edilir. Ayrıca \(\Delta(2)=20\) olduğu için \(T_3=22\) ve \(H(3)=47\) ile
$$22+47=69$$
çıkar. Bunlar brute-force checkpoint ile birebir uyuşur.
Asıl pahalı görünen kısım
$$\sum \lfloor n/d\rfloor$$
toplamlarıdır. Kod bunları bölüm bloklarıyla toplar: aynı bölüm değerini veren ardışık \(d\) aralıkları tek seferde eklenir. Bu, klasik harmonik gruplama tekniğidir.
1) Asal sınırı. Önce \(k\). asal için \(n(\log n+\log\log n)\) tipi bir üst sınır tahmini alınır; yetmezse sınır büyütülür.
2) Lineer sieve. Aynı anda hem asal listesi hem de \(\operatorname{spf}(n)\) tablosu üretilir.
3) Floor-sum yardımcıları. floor_sum_upto() ve floor_sum_range() bölüm bloklarını uygular.
4) Kapalı form fonksiyonları. candidate_delta(), \(\Delta(n)\)'i; prime_hit_offset(), \(H(p)\)'yi hesaplar.
5) Prefix yürüyüşü. Ana döngü adayları tek tek geçer, \(T_n\)'yi toplar ve hedef asal \(p_k\)'ye ulaşınca \(T_{p_k}+H(p_k)\) döndürür.
6) Güçlü doğrulama. C++ sürümünde ayrıca gerçek otomatonu birebir simüle eden bir doğrulama bölümü vardır; ilk 10 çıktı ve birkaç küçük hedef indeks kapalı formla karşılaştırılır.
Sieve aşaması seçilen üst sınır \(B\) için \(O(B)\) zaman ve bellek ister. Her aday bundan sonra yalnızca birkaç harmonik floor-sum hesaplar; bunların her biri yaklaşık \(O(\sqrt n)\) blok adımıyla çalışır. Bu, nihai cevap için yaklaşık \(1.5\times10^{15}\) ham geçiş gerektirecek doğrudan FRACTRAN simülasyonundan çok daha hızlıdır.
El autómata del problema 308 es una codificación gráfica del programa FRACTRAN generador de primos de Conway. Simularlo paso a paso es inviable mucho antes de llegar a la salida prima número \(10001\). La idea correcta es entender analíticamente un ciclo completo de candidato y contar pasos sin recorrer cada transición.
El estado puede verse como los exponentes de
$$2,3,5,7,11,13,17,19,23,29,$$
es decir, como el entero
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
El autómata “emite” un valor exactamente cuando el estado es una potencia pura de \(2\):
$$N=2^m.$$
En ese caso el valor emitido es el exponente \(m\). El checkpoint de fuerza bruta en C++ confirma que las primeras salidas son
$$2,3,5,7,11,13,17,19,23,29,\dots$$
en los pasos
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
La dinámica vuelve repetidamente a un estado canónico que significa “ahora se está probando el candidato \(n\)”. El primer índice de paso de ese estado se denota por
$$T_n.$$
El código fija
$$T_2=2,$$
porque tras dos transiciones iniciales desde la semilla se llega al inicio estándar del candidato \(2\).
Un ciclo completo para \(n\) actúa como una rutina determinista de prueba de divisores:
Preparación: construye la estructura de control para \(n\).
Fase central: prueba divisores \(d\) en orden descendente.
Salida: si encuentra divisor, pasa al candidato \(n+1\); si no existe ninguno, ejecuta una rama especial de primo y emite \(2^n\).
Por eso todo el ciclo puede resumirse con
$$\Delta(n)=T_{n+1}-T_n$$
y, para primos \(p\), con el desplazamiento
$$H(p).$$
Sea
$$s=\operatorname{spf}(n)$$
el menor factor primo de \(n\). Entonces el primer divisor que se encuentra al escanear hacia abajo es el mayor divisor propio
$$d_*=\frac{n}{s}.$$
Todos los enteros
$$d=n-1,n-2,\dots,d_*+1$$
son necesariamente no divisores, y el escaneo se detiene justo en \(d_*\). Por eso la fórmula del caso compuesto se separa de manera natural en “pruebas fallidas por encima de \(d_*\)” y “rama terminal exitosa en \(d_*\)”.
El código ya ha comprimido todo el grafo de estados de un ciclo en sumas exactas.
Para un candidato general \(n\), el salto al comienzo de \(n+1\) es
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
Las tres piezas corresponden a preparación, pruebas fallidas y rama terminal compuesta.
Si \(p\) es primo, no hay divisor en \(2,\dots,p-1\), así que
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
El término final \(6p+2\) es la propia rama prima.
Con \(\Delta(n)\), los tiempos de inicio satisfacen
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
Si \(p_k\) es el \(k\)-ésimo primo, la respuesta final es
$$T_{p_k}+H(p_k).$$
Por ejemplo, \(H(2)=17\), así que el primer impacto primo ocurre en
$$2+17=19.$$
Además, \(\Delta(2)=20\), luego \(T_3=22\), y con \(H(3)=47\) obtenemos
$$22+47=69,$$
exactamente como en la simulación bruta.
La parte aparentemente cara es
$$\sum \lfloor n/d\rfloor.$$
La implementación la evalúa por bloques de cociente: cuando varios divisores consecutivos producen el mismo cociente, se suman de una vez. Ese es el truco clásico de agrupación armónica.
1) Cota para primos. Primero estima una cota superior para el \(k\)-ésimo primo con una fórmula tipo \(n(\log n+\log\log n)\).
2) Criba lineal. La criba produce simultáneamente la lista de primos y la tabla \(\operatorname{spf}(n)\).
3) Ayudantes de floor-sum. floor_sum_upto() y floor_sum_range() implementan la suma por bloques.
4) Fórmulas cerradas. candidate_delta() calcula \(\Delta(n)\) y prime_hit_offset() calcula \(H(p)\).
5) Recorrido prefijo. El bucle principal avanza candidato por candidato hasta \(p_k\) y devuelve \(T_{p_k}+H(p_k)\).
6) Validación fuerte. La versión en C++ también contiene un simulador literal del autómata y compara la fórmula cerrada con los primeros impactos primos reales.
La criba cuesta \(O(B)\) en tiempo y memoria hasta la cota prima \(B\). Después, cada candidato solo necesita unas pocas sumas armónicas, cada una con unas \(O(\sqrt n)\) iteraciones por bloques. Es muchísimo más rápido que la simulación FRACTRAN literal, que necesitaría del orden de \(1.5\times10^{15}\) transiciones para la respuesta final.
第 308 题中的自动机,本质上是 Conway 质数生成 FRACTRAN 程序的图形化编码。如果逐步模拟,远在第 \(10001\) 个质数输出之前就已经无法承受。真正有效的方法是把一个完整的候选数循环解析出来,然后直接计算步数。
机器状态可以写成下面这些素数的指数向量:
$$2,3,5,7,11,13,17,19,23,29.$$
等价地,也就是整数
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
自动机只有在状态是纯粹的二次幂时才“输出”:
$$N=2^m.$$
此时输出值就是指数 \(m\)。C++ 中的 brute-force 检查表明,前几个输出为
$$2,3,5,7,11,13,17,19,23,29,\dots$$
对应步数为
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
机器会不断回到一个标准状态,含义是“现在开始测试候选数 \(n\)”。这个状态第一次出现的步号记作
$$T_n.$$
源码使用
$$T_2=2,$$
意思是从初始种子出发经过两步后,已经进入了测试候选 \(2\) 的标准起点。
对固定候选 \(n\),整段动力学其实就是一个确定性的除数测试流程:
准备阶段:构造与 \(n\) 对应的控制结构。
中间阶段:按从大到小的顺序测试除数 \(d\)。
结束阶段:若找到除数,则进入候选 \(n+1\);若始终找不到,则进入特殊的质数分支并输出 \(2^n\)。
因此整个循环只需要两个精确量:
$$\Delta(n)=T_{n+1}-T_n,$$
以及当 \(p\) 为质数时的输出偏移
$$H(p).$$
记
$$s=\operatorname{spf}(n)$$
为 \(n\) 的最小质因子。那么从大往小扫描时,第一个真正遇到的因子就是最大的真因子
$$d_*=\frac{n}{s}.$$
因此
$$d=n-1,n-2,\dots,d_*+1$$
这些值全都必然不是因子,扫描恰好在 \(d_*\) 处停止。这就解释了为什么合数情形的公式自然分成“上方所有失败测试”与“最后成功分支”两部分。
代码已经把一个候选循环的整张状态图压缩成了精确的求和表达式。
对一般候选 \(n\),从 \(n\) 的起点跳到 \(n+1\) 的起点需要
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
其中三部分分别对应准备、所有失败的除数测试以及最后的合数终止分支。
若 \(p\) 为质数,则 \(2,\dots,p-1\) 中根本没有因子,于是质数输出偏移为
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
最后的 \(6p+2\) 就是质数输出支路本身。
已知 \(\Delta(n)\) 后,有递推
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
若 \(p_k\) 是第 \(k\) 个质数,那么题目答案就是
$$T_{p_k}+H(p_k).$$
例如 \(H(2)=17\),所以第一次质数命中发生在
$$2+17=19.$$
又因为 \(\Delta(2)=20\),所以 \(T_3=22\),再加上 \(H(3)=47\),得到
$$22+47=69,$$
与暴力模拟完全一致。
看起来最贵的部分是
$$\sum \lfloor n/d\rfloor.$$
实现中用“商分块”来加速:若一段连续的 \(d\) 给出相同的商 \(\lfloor n/d\rfloor\),就整段一起累计。这是经典的调和分块技巧。
1) 质数上界估计。 先用 \(n(\log n+\log\log n)\) 型估计给出第 \(k\) 个质数的上界,不够时再扩大。
2) 线性筛。 同时得到质数表和 \(\operatorname{spf}(n)\) 表。
3) floor-sum 辅助函数。 floor_sum_upto() 与 floor_sum_range() 负责商分块求和。
4) 闭式计算。 candidate_delta() 计算 \(\Delta(n)\),prime_hit_offset() 计算 \(H(p)\)。
5) 前缀推进。 主循环按候选数推进 \(T_n\),直到第 \(k\) 个质数 \(p_k\),最后返回 \(T_{p_k}+H(p_k)\)。
6) 强校验。 C++ 代码还保留了字面意义上的自动机模拟器,并将前若干个真实命中与闭式公式逐一比对。
筛法部分在质数上界 \(B\) 内需要 \(O(B)\) 时间和空间。之后每个候选只需要少量 floor-sum,而每个 floor-sum 通过分块大约只用 \(O(\sqrt n)\) 次块操作。这比直接 FRACTRAN 模拟快得多,后者对最终答案大约需要 \(1.5\times10^{15}\) 次状态转移。
Автомат из задачи 308 является графическим представлением простогенерирующей программы FRACTRAN Конвея. Прямая пошаговая симуляция слишком медленна. Поэтому нужно аналитически разобрать один полный цикл кандидата и считать шаги без явного прохождения всех переходов.
Состояние можно записать через показатели простых чисел
$$2,3,5,7,11,13,17,19,23,29,$$
то есть как число
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
Автомат “печатает” значение тогда и только тогда, когда состояние является чистой степенью двойки:
$$N=2^m.$$
Тогда печатаемое число равно показателю \(m\). Брутфорс-проверка в C++ подтверждает первые выходы
$$2,3,5,7,11,13,17,19,23,29,\dots$$
на шагах
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
Динамика регулярно возвращается в каноническое состояние, означающее: «сейчас проверяется кандидат \(n\)». Первый шаг этого состояния обозначается
$$T_n.$$
В коде используется
$$T_2=2,$$
то есть после двух начальных переходов машина уже находится в стандартной стартовой конфигурации для кандидата \(2\).
Полный цикл для \(n\) работает как детерминированная проверка делителей:
Подготовка: строится управляющая конфигурация для \(n\).
Средняя часть: делители \(d\) проверяются по убыванию.
Завершение: если делитель найден, машина переходит к кандидату \(n+1\); если делителей нет, выполняется специальная простая ветвь и печатается \(2^n\).
Поэтому весь цикл описывается двумя точными величинами:
$$\Delta(n)=T_{n+1}-T_n$$
и, для простого \(p\), смещением попадания
$$H(p).$$
Пусть
$$s=\operatorname{spf}(n)$$
— наименьший простой делитель \(n\). Тогда первый делитель, который встретится при движении сверху вниз, это наибольший собственный делитель
$$d_*=\frac{n}{s}.$$
Все значения
$$d=n-1,n-2,\dots,d_*+1$$
точно не делят \(n\), и поиск останавливается ровно на \(d_*\). Именно поэтому формула для составного случая естественно разбивается на “все неудачные проверки выше \(d_*\)” и “последнюю успешную ветвь в \(d_*\)”.
Код уже свернул весь граф состояний одного цикла в точные суммы.
Для общего кандидата \(n\):
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
Три части соответствуют подготовке, всем неудачным тестам делимости и завершающей составной ветви.
Если \(p\) — простое, то делителей в \(2,\dots,p-1\) нет, и потому
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
Последний член \(6p+2\) — это сама простая ветвь.
После вычисления \(\Delta(n)\) имеем
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
Если \(p_k\) — \(k\)-е простое число, то ответ задачи равен
$$T_{p_k}+H(p_k).$$
Например, \(H(2)=17\), значит первое простое попадание происходит на шаге
$$2+17=19.$$
Далее \(\Delta(2)=20\), откуда \(T_3=22\), а с \(H(3)=47\) получаем
$$22+47=69,$$
что точно совпадает с грубой симуляцией.
На вид дорогая часть — это суммы вида
$$\sum \lfloor n/d\rfloor.$$
Они считаются блоками одинакового частного: если подряд идущие \(d\) дают один и тот же \(\lfloor n/d\rfloor\), код суммирует их сразу. Это стандартный гармонический прием.
1) Оценка границы для простых. Сначала берется верхняя оценка для \(k\)-го простого через формулу типа \(n(\log n+\log\log n)\).
2) Линейное решето. Оно строит и список простых, и таблицу \(\operatorname{spf}(n)\).
3) Помощники для floor-sum. floor_sum_upto() и floor_sum_range() реализуют блочное суммирование.
4) Закрытые формулы. candidate_delta() вычисляет \(\Delta(n)\), а prime_hit_offset() — \(H(p)\).
5) Префиксный проход. Главный цикл идет по кандидатам до \(p_k\), накапливая \(T_n\), и возвращает \(T_{p_k}+H(p_k)\).
6) Сильная проверка. В C++ есть и буквальный симулятор автомата; он сравнивает первые 10 реальных простых попаданий и несколько малых целевых индексов с формульным решением.
Решето требует \(O(B)\) времени и памяти до выбранной простой границы \(B\). После этого каждый кандидат использует лишь несколько гармонических floor-сумм, каждая примерно за \(O(\sqrt n)\) блоков. Это несравнимо быстрее прямой FRACTRAN-симуляции, которая для окончательного ответа потребовала бы порядка \(1.5\times10^{15}\) переходов.
الأوتومات في المسألة 308 هو تمثيل رسومي لبرنامج Conway المولّد للأوليات بطريقة FRACTRAN. المحاكاة الحرفية خطوة بخطوة تصبح بطيئة جدًا قبل الوصول إلى الناتج الأولي رقم \(10001\). لذلك يجب تحليل دورة مرشح كاملة تحليليًا وعدّ الخطوات من دون المرور على كل انتقال منفرد.
يمكن تمثيل الحالة بأسس الأعداد الأولية
$$2,3,5,7,11,13,17,19,23,29,$$
أي بالعدد
$$N=2^a3^b5^c7^d11^e13^f17^g19^h23^i29^j.$$
ويُصدر الأوتومات قيمة فقط عندما تكون الحالة قوة صافية للعدد \(2\):
$$N=2^m.$$
وعندها تكون القيمة المطبوعة هي الأس \(m\). التحقق brute-force في كود ++C يؤكد أن المخرجات الأولى هي
$$2,3,5,7,11,13,17,19,23,29,\dots$$
وعند الخطوات
$$19,69,281,710,2375,3893,8102,11361,19268,36981,\dots$$
الديناميكا تعود باستمرار إلى حالة قياسية معناها: «الآن يجري اختبار المرشح \(n\)». رقم الخطوة الأولى لهذه الحالة نرمز له بـ
$$T_n.$$
ويستخدم الكود القيمة
$$T_2=2,$$
أي أنه بعد انتقالين ابتدائيين من البذرة تدخل الآلة في وضع البداية المعياري للمرشح \(2\).
الدورة الكاملة للمرشح \(n\) تتصرف فعليًا كاختبار قواسم حتمي:
مرحلة الإعداد: تُبنى البنية التحكّمية الخاصة بـ \(n\).
المرحلة الوسطى: تُفحَص القواسم \(d\) بترتيب تنازلي.
الإنهاء: إذا وُجد قاسم انتقلت الآلة إلى المرشح \(n+1\)، وإذا لم يوجد قاسم سلكت فرعًا خاصًا بالأولي وطبعت \(2^n\).
ولهذا تكفي كميتان دقيقتان لوصف الدورة:
$$\Delta(n)=T_{n+1}-T_n$$
وإزاحة الإصابة للمرشح الأولي
$$H(p).$$
إذا كان
$$s=\operatorname{spf}(n)$$
هو أصغر عامل أولي لـ \(n\)، فإن أول قاسم تلتقيه عملية المسح من الأعلى إلى الأسفل هو أكبر قاسم حقيقي:
$$d_*=\frac{n}{s}.$$
وبالتالي فإن جميع القيم
$$d=n-1,n-2,\dots,d_*+1$$
ليست قواسم قطعًا، ويتوقف المسح تحديدًا عند \(d_*\). ولهذا تنقسم صيغة العدد المركب طبيعيًا إلى «كل اختبارات الفشل فوق \(d_*\)» ثم «الفرع النهائي الناجح عند \(d_*\)».
الكود قد اختزل كامل مخطط الحالات في دورة واحدة إلى مجاميع دقيقة.
للمرشح العام \(n\)، تكون القفزة إلى بداية المرشح التالي:
$$\Delta(n)=(2n-1)+\sum_{d=d_*+1}^{n-1}\bigl(6n+2+2\lfloor n/d\rfloor\bigr)+\bigl(5n+d_*+2\lfloor n/d_*\rfloor+2\bigr).$$
وتقابل الأجزاء الثلاثة الإعداد، واختبارات القواسم الفاشلة، والفرع المركب النهائي.
أما إذا كان \(p\) أوليًا، فلا يوجد قاسم بين \(2\) و\(p-1\)، ولذلك
$$H(p)=(2p-1)+\sum_{d=2}^{p-1}\bigl(6p+2+2\lfloor p/d\rfloor\bigr)+(6p+2).$$
والحد الأخير \(6p+2\) هو فرع الإخراج الأولي نفسه.
بعد معرفة \(\Delta(n)\) تصبح أزمنة البداية
$$T_{n+1}=T_n+\Delta(n),\qquad T_2=2.$$
وإذا كان \(p_k\) هو العدد الأولي رقم \(k\)، فالجواب النهائي هو
$$T_{p_k}+H(p_k).$$
مثلًا \(H(2)=17\)، ولذلك تقع أول إصابة أولية عند
$$2+17=19.$$
وكذلك \(\Delta(2)=20\)، ومن ثم \(T_3=22\)، ومع \(H(3)=47\) نحصل على
$$22+47=69,$$
وهو ما يطابق المحاكاة المباشرة تمامًا.
الجزء الذي يبدو مكلفًا هو
$$\sum \lfloor n/d\rfloor.$$
لكن التنفيذ يحسبه على شكل كتل من القيم التي تشترك في خارج القسمة نفسه. فإذا أعطت مجموعة متتالية من \(d\) القيمة نفسها لـ \(\lfloor n/d\rfloor\)، تُجمَع دفعة واحدة. هذه هي حيلة التجميع التوافقي المعروفة.
1) تقدير حد أولي علوي. يبدأ الحل بتقدير حد أعلى للعدد الأولي رقم \(k\) بصيغة من نوع \(n(\log n+\log\log n)\).
2) غربال خطي. يُنتج الغربال قائمة الأوليات وجدول \(\operatorname{spf}(n)\) معًا.
3) دوال floor-sum. تطبّق floor_sum_upto() وfloor_sum_range() تجميع القيم على شكل كتل.
4) الصيغ المغلقة. تحسب candidate_delta() الكمية \(\Delta(n)\)، وتحسب prime_hit_offset() الكمية \(H(p)\).
5) السير عبر البادئة. الحلقة الرئيسية تتقدم مرشحًا بعد مرشح حتى \(p_k\)، ثم تعيد \(T_{p_k}+H(p_k)\).
6) تحقق قوي. يحتوي كود ++C أيضًا على محاكٍ حرفي للأوتومات ويقارن أوائل الإصابات الأولية مع الصيغ المغلقة.
الغربال يكلف \(O(B)\) زمنًا وذاكرة حتى الحد الأولي \(B\). بعد ذلك، يحتاج كل مرشح فقط إلى عدد قليل من مجاميع floor-sum، وكل واحد منها يعمل تقريبًا في \(O(\sqrt n)\) خطوة كتلية. وهذا أسرع بكثير من محاكاة FRACTRAN الحرفية التي كانت ستتطلب نحو \(1.5\times10^{15}\) انتقالًا للوصول إلى الجواب النهائي.