A frog starts uniformly on one of the squares \(1,2,\dots,500\). It then produces the croak pattern
$$s=\texttt{PPPPNNPPPNPPNPN}$$
of length \(L=15\). On a prime square it says P with probability \(2/3\) and N with probability \(1/3\); on a non-prime square the probabilities are reversed. After each croak except the last one, the frog jumps to a neighboring square: in the interior it chooses left or right with probability \(1/2\), while at squares \(1\) and \(500\) the move is forced. We want the exact probability that the whole croak sequence matches \(s\).
1) This is a finite hidden-state process.
The hidden state is the current square \(i\in\{1,\dots,500\}\). The observed symbol at time \(t\) is \(s_t\in\{P,N\}\). Because the next distribution depends only on the current square, the problem is a small Markov-style dynamic program: we propagate the probability mass over all squares while reading the croaks one by one.
2) Prime information is precomputed once.
Let
$$\pi(i)=\begin{cases}1,&\text{if }i\text{ is prime},\\0,&\text{otherwise}.\end{cases}$$
The code builds this array with a sieve up to \(500\). We need it at every croak, so doing the primality work once is the natural preprocessing step.
3) Separate numerators from the global denominator.
If we used floating point directly, we would repeatedly mix factors \(2/3\), \(1/3\), \(1/2\), and \(1\). The implementation instead keeps only integer numerators and stores the full denominator outside the state vector.
Start with integer weights
$$v_0(i)=1\qquad(1\le i\le 500),$$
and a global denominator
$$D_0=500.$$
This represents the uniform initial distribution \(1/500\) on every square.
4) Croak emission is just a multiplication by \(2\) or \(1\).
For the \(t\)-th croak define the integer emission factor
$$e_t(i)=\begin{cases} 2,&\text{if }s_t=P\text{ and }i\text{ is prime},\\ 2,&\text{if }s_t=N\text{ and }i\text{ is non-prime},\\ 1,&\text{otherwise}. \end{cases}$$
The actual probability factor is \(e_t(i)/3\). So after reading croak \(t\), but before moving, the integer weights become
$$u_t(i)=e_t(i)\,v_{t-1}(i),$$
and the global denominator gains one factor \(3\):
$$D\leftarrow 3D.$$
5) The jump step is also encoded with integers.
After croaks \(1\) through \(14\), the frog jumps once. Instead of dividing by \(2\) locally at every interior square, the code pulls out one global factor \(2\) and moves integer contributions:
$$v_t=T(u_t).$$
For a destination square \(j\), the transition is
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
Why do the edge contributions carry a factor \(2\)? Because the move from square \(1\) to square \(2\) is forced, so probability \(1\) is stored as \(2/2\) when we pull out one common denominator \(2\) for the whole step. After each jump we update
$$D\leftarrow 2D.$$
6) The full recurrence is therefore exact.
For \(t=1,2,\dots,15\):
Emission:
$$u_t(i)=e_t(i)\,v_{t-1}(i),\qquad D\leftarrow 3D.$$
Jump, only if \(t<15\):
$$v_t=T(u_t),\qquad D\leftarrow 2D.$$
After the last croak there is no jump, so the final probability is
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
The code finally reduces this fraction by \(\gcd\).
7) A one-croak example makes the scaling clear.
There are exactly \(95\) primes in \(\{1,\dots,500\}\), hence \(405\) non-primes. For the single croak \(P\),
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{190+405}{1500} =\frac{595}{1500} =\frac{119}{300}.$$
This is exactly the first checkpoint in the program. By complementarity,
$$\Pr(N)=1-\Pr(P)=\frac{181}{300}.$$
8) Why integer arithmetic is enough.
Every path through the process contributes a product of factors taken from
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
So every path probability has denominator dividing
$$500\cdot 3^{15}\cdot 2^{14}.$$
That means we lose nothing by storing only the common numerator weights as arbitrary-precision integers. This is exactly why the implementation uses cpp_int.
9) Worked DP interpretation.
At each stage, the vector \(v_t\) says: "after processing the first \(t\) croaks and the first \(t\) jumps, how much integer numerator mass is sitting on each square?" The emission step filters that mass according to whether the current square is prime or not, and the jump step redistributes it to neighboring squares. Summing the final vector gives the numerator of the total matching probability.
1) Sieve primality for \(1,\dots,500\).
2) Initialize all numerator weights to \(1\) and set the denominator to \(500\).
3) For each croak, multiply each square by the matching emission factor \(2\) or \(1\), then multiply the denominator by \(3\).
4) If the croak is not the last one, redistribute mass to neighboring squares using the integer transition above, then multiply the denominator by \(2\).
5) Sum the final weights, reduce the fraction by \(\gcd\), and print the exact result.
If \(N=500\) and \(L=15\), the dynamic program performs one pass over all squares for each croak and each jump. Hence the cost is
$$O(NL)$$
time and
$$O(N)$$
memory. For this problem that is tiny; the exact arithmetic is completely practical.
The code checks
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
and verifies that these two probabilities sum to \(1\).
For the full sequence
$$\texttt{PPPPNNPPPNPPNPN},$$
the exact reduced probability is
$$\boxed{\frac{199740353}{29386561536000}}.$$
Ein Frosch startet gleichverteilt auf einem Feld aus \(\{1,2,\dots,500\}\). Danach erzeugt er die Lautfolge
$$s=\texttt{PPPPNNPPPNPPNPN}$$
mit \(L=15\) Lauten. Auf einem Primzahlfeld sagt er P mit Wahrscheinlichkeit \(2/3\) und N mit \(1/3\); auf einem Nicht-Primzahlfeld genau umgekehrt. Zwischen zwei Lauten springt er zu einem Nachbarfeld: im Inneren links oder rechts mit \(1/2\), an den Raendern \(1\) und \(500\) jeweils deterministisch zum einzigen Nachbarn. Gesucht ist die exakte Wahrscheinlichkeit, dass die ganze Lautfolge mit \(s\) uebereinstimmt.
1) Versteckter Zustand = aktuelles Feld.
Der einzige relevante Zustand ist die aktuelle Position \(i\). Darum reicht eine kleine dynamische Programmierung ueber alle \(500\) Felder: Wir halten nach jedem Laut fest, wie viel Wahrscheinlichkeitsmasse auf jedem Feld liegt.
2) Primzahlen werden einmal vorgesiebt.
Definiere
$$\pi(i)=\begin{cases}1,&\text{falls }i\text{ prim ist},\\0,&\text{sonst}.\end{cases}$$
Die Implementierung berechnet dieses Array per Sieb bis \(500\). Danach ist jeder Lautschritt nur noch ein schneller Tabellenzugriff.
3) Gemeinsamen Nenner ausklammern.
Die Faktoren \(2/3\), \(1/3\), \(1/2\) und \(1\) werden nicht als Gleitkommazahlen propagiert. Stattdessen speichert der Code nur ganzzahlige Zaehlergewichte und fuehrt einen globalen Nenner separat.
Zu Beginn:
$$v_0(i)=1\qquad(1\le i\le 500),\qquad D_0=500.$$
Das repraesentiert die uniforme Startverteilung \(1/500\).
4) Lautwahrscheinlichkeiten werden zu Faktoren \(2\) oder \(1\).
Fuer den \(t\)-ten Laut sei
$$e_t(i)=\begin{cases} 2,&\text{wenn }s_t=P\text{ und }i\text{ prim ist},\\ 2,&\text{wenn }s_t=N\text{ und }i\text{ nicht prim ist},\\ 1,&\text{sonst}. \end{cases}$$
Der echte Wahrscheinlichkeitsfaktor ist \(e_t(i)/3\). Also gilt vor dem Sprung:
$$u_t(i)=e_t(i)\,v_{t-1}(i),\qquad D\leftarrow 3D.$$
5) Auch der Sprung wird ganzzahlig kodiert.
Nach Lauten \(1\) bis \(14\) springt der Frosch. Statt lokal durch \(2\) zu teilen, zieht der Code einen gemeinsamen Faktor \(2\) aus dem ganzen Schritt heraus. Damit ergibt sich
$$v_t=T(u_t),$$
wobei fuer Zielfeld \(j\) gilt:
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
Die Faktoren \(2u_t(1)\) und \(2u_t(500)\) erscheinen, weil ein Rand-Sprung Wahrscheinlichkeit \(1\) hat und deshalb als \(2/2\) gespeichert wird. Danach:
$$D\leftarrow 2D.$$
6) Damit ist die Rekurrenz exakt.
Fuer \(t=1,\dots,15\): erst Emission, dann fuer \(t<15\) der Sprung. Nach dem letzten Laut gibt es keinen Sprung mehr. Also ist
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
Zum Schluss wird mit dem ggT gekuerzt.
7) Ein Ein-Laut-Beispiel.
In \(\{1,\dots,500\}\) gibt es genau \(95\) Primzahlen und \(405\) Nicht-Primzahlen. Daher
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{119}{300},$$
und folglich
$$\Pr(N)=\frac{181}{300}.$$
Genau diese Werte nutzt der Code als Checkpoints.
8) Warum ganze Zahlen genuegen.
Jeder Pfad liefert nur Faktoren aus
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
Daher teilt jeder Pfadnenner den Gesamtwert
$$500\cdot 3^{15}\cdot 2^{14}.$$
Es gibt also keinen Verlust, wenn nur die Zaehler mit beliebig grossen Integern gespeichert werden.
1) Primzahlmarkierungen bis \(500\) per Sieb aufbauen.
2) Zaehlergewichte mit \(1\) initialisieren und Nenner \(500\) setzen.
3) Fuer jeden Laut jedes Feld mit Faktor \(2\) oder \(1\) multiplizieren; Nenner mit \(3\) multiplizieren.
4) Falls es nicht der letzte Laut ist, Nachbaruebergaenge per ganzzahligem Schritt ausfuehren; Nenner mit \(2\) multiplizieren.
5) Endvektor aufsummieren, mit dem Nenner kuerzen und die exakte Wahrscheinlichkeit ausgeben.
Bei \(N=500\) und \(L=15\) kostet die Rechnung
$$O(NL)$$
Zeit und
$$O(N)$$
Speicher. Das ist sehr klein; die exakte Berechnung ist problemlos moeglich.
Geprueft werden
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
sowie \(\Pr(P)+\Pr(N)=1\).
Fuer die volle Folge
$$\texttt{PPPPNNPPPNPPNPN}$$
erhaelt man die reduzierte exakte Wahrscheinlichkeit
$$\boxed{\frac{199740353}{29386561536000}}.$$
Bir kurbaga \(\{1,2,\dots,500\}\) karelerinden birinde esit olasilikla basliyor. Daha sonra
$$s=\texttt{PPPPNNPPPNPPNPN}$$
ses dizisini cikarmasi bekleniyor; uzunluk \(L=15\). Kurbaga asal karedeyse P sesini \(2/3\), N sesini \(1/3\) olasilikla; asal olmayan karedeyse bunun tersini verir. Son sesten onceki her adimda komsu kareye ziplar: orta karelerde sola veya saga \(1/2\), sinirlarda ise tek komsuya zorunlu gecer. Amac, butun dizinin tam olasiligini bulmaktir.
1) Gizli durum yalnizca konumdur.
Sistemin butun belleksiz yapisi, mevcut karenin numarasi ile belirlenir. Bu nedenle cozum, her sesten sonra \(500\) karenin uzerindeki olasilik kutlesini guncelleyen kucuk bir dinamik programdir.
2) Asallik bilgisi bir kez hesaplanir.
$$\pi(i)=\begin{cases}1,&\text{eger }i\text{ asalsa},\\0,&\text{degilse}.\end{cases}$$
Kod once \(500\)'e kadar bir sieve kurar. Sonraki butun adimlarda her kare icin yalnizca bu tabloya bakilir.
3) Ortak paydayi disarida tutmak en temiz yoldur.
Dogrudan kayan nokta ile gitmek yerine kod sadece tamsayi paylari saklar. Baslangicta
$$v_0(i)=1\qquad(1\le i\le 500),$$
ve ortak payda
$$D_0=500$$
secilir. Bu, her kare icin \(1/500\) baslangic olasiligini temsil eder.
4) Ses adimi, her kareyi \(2\) veya \(1\) ile carpma isidir.
\(t\). ses icin tamsayi emisyon katsayisi
$$e_t(i)=\begin{cases} 2,&\text{eger }s_t=P\text{ ve }i\text{ asal ise},\\ 2,&\text{eger }s_t=N\text{ ve }i\text{ asal degilse},\\ 1,&\text{aksi halde}. \end{cases}$$
Gercek olasilik carpani \(e_t(i)/3\) oldugu icin, ses sonrasindaki fakat ziplamadan onceki agirliklar
$$u_t(i)=e_t(i)\,v_{t-1}(i)$$
olur ve ortak payda
$$D\leftarrow 3D$$
seklinde guncellenir.
5) Ziplamayi da tamsayi aktarimiyla yazabiliriz.
Ilk \(14\) sesten sonra kurbaga bir kez ziplar. Kod, orta karelerdeki \(1/2\) olasiligini tek tek bolmek yerine tum adim icin ortak bir \(2\) paydasi cikarir ve tamsayi akislari tasir:
$$v_t=T(u_t).$$
Hedef kare \(j\) icin gecis tam olarak
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500 \end{cases}$$
seklindedir. Sinirdaki zorunlu hareketin katsayisinin \(2\) olmasinin nedeni, olasilik \(1\)'in ortak \(/2\) olceginde \(2/2\) olarak yazilmasidir. Bu adimdan sonra
$$D\leftarrow 2D$$
yapilir.
6) Butun recurrence boylece tamdir.
\(t=1,2,\dots,15\) icin once emisyon uygulanir. Eger \(t<15\) ise arkasindan ziplamaya gecilir. Son sesten sonra hareket olmadigi icin nihai olasilik
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}$$
olur. Kod en sonda bu kesri OBEB ile sadelestirir.
7) Tek seslik ornek olceklendirmeyi cok net gosterir.
\(\{1,\dots,500\}\) icinde tam \(95\) asal, \(405\) asal olmayan sayi vardir. O halde tek ses \(P\) icin
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{190+405}{1500} =\frac{595}{1500} =\frac{119}{300}.$$
Buradan tamamlayici olasilik
$$\Pr(N)=\frac{181}{300}$$
elde edilir. Kodun ilk checkpoint'i tam olarak budur.
8) Neden tamsayi aritmetigi yeterli?
Her yolun carpiminda sadece su faktorler bulunur:
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
Dolayisiyla her yol olasiliginin paydasi
$$500\cdot 3^{15}\cdot 2^{14}$$
degerini boler. Bu da tum hesabin, ortak paydayi disarida tutup sadece paylari cpp_int ile izleyerek kayipsiz yapilabilecegi anlamina gelir.
9) DP vektorunun anlami.
\(v_t\) vektoru, ilk \(t\) sesi ve ilk \(t\) ziplamayi isledikten sonra her karede biriken tamsayi pay kutlesini verir. Emisyon adimi bu kutleyi asallik ile uyuma gore filtreler; ziplamaysa komsulara dagitir. Son vektorun toplamı, tum dizi eslesmesinin payidir.
1) \(1\) ile \(500\) arasindaki asallik bilgisini sieve ile hesapla.
2) Tum kare agirliklarini \(1\) ile baslat, paydayi \(500\) yap.
3) Her seste, ilgili kareyi \(2\) veya \(1\) ile carp; paydayi \(3\) ile carp.
4) Son ses degilse, komsu gecislerini tamsayi aktarimiyla uygula; paydayi \(2\) ile carp.
5) Final vektorunu topla, kesri sadelestir ve exact sonucu yazdir.
Genel maliyet
$$O(NL)$$
zaman ve
$$O(N)$$
bellektir. Bu problemde \(N=500\), \(L=15\) oldugu icin exact hesap cok rahattir.
Kod su degerleri dogrular:
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
ve bunlarin toplaminin \(1\) oldugunu kontrol eder.
Tam dizi
$$\texttt{PPPPNNPPPNPPNPN}$$
icin sadelestirilmis exact olasilik
$$\boxed{\frac{199740353}{29386561536000}}$$
olarak bulunur.
Una rana empieza de forma uniforme en una casilla de \(\{1,2,\dots,500\}\). Luego debe producir la secuencia
$$s=\texttt{PPPPNNPPPNPPNPN}$$
de longitud \(L=15\). Si esta en una casilla prima, emite P con probabilidad \(2/3\) y N con \(1/3\); si esta en una casilla no prima, las probabilidades se invierten. Despues de cada croar, salvo el ultimo, la rana salta a una casilla vecina: en el interior va a izquierda o derecha con probabilidad \(1/2\), y en los extremos \(1\) y \(500\) el salto es forzado. Se pide la probabilidad exacta de observar toda la secuencia \(s\).
1) El estado oculto es solo la posicion actual.
Como el siguiente paso depende unicamente de la casilla actual, basta una DP sobre las \(500\) posiciones: mantenemos la masa de probabilidad en cada casilla mientras leemos la secuencia.
2) La primalidad se precalcula una sola vez.
Definimos
$$\pi(i)=\begin{cases}1,&\text{si }i\text{ es primo},\\0,&\text{en otro caso}.\end{cases}$$
El codigo construye esta tabla con una criba hasta \(500\).
3) Sacar el denominador comun simplifica todo.
En vez de propagar fracciones, el programa guarda solo numeradores enteros y mantiene aparte un denominador global. Al inicio:
$$v_0(i)=1\qquad(1\le i\le 500),\qquad D_0=500.$$
Eso representa la distribucion uniforme inicial.
4) Cada croar equivale a multiplicar por \(2\) o por \(1\).
Para el paso \(t\), definimos
$$e_t(i)=\begin{cases} 2,&\text{si }s_t=P\text{ y }i\text{ es primo},\\ 2,&\text{si }s_t=N\text{ y }i\text{ no es primo},\\ 1,&\text{si no}. \end{cases}$$
El factor probabilistico real es \(e_t(i)/3\). Por tanto, despues del croar y antes del salto:
$$u_t(i)=e_t(i)\,v_{t-1}(i),\qquad D\leftarrow 3D.$$
5) El salto tambien se expresa con enteros.
Tras los primeros \(14\) croares, la rana salta. El codigo extrae un factor comun \(2\) del paso de movimiento y transfiere contribuciones enteras:
$$v_t=T(u_t).$$
Para la casilla destino \(j\):
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
Los factores \(2\) en los bordes aparecen porque un salto forzado de probabilidad \(1\) se guarda como \(2/2\) al sacar un denominador comun \(2\). Luego:
$$D\leftarrow 2D.$$
6) La recurrencia completa es exacta.
Para \(t=1,\dots,15\), primero se aplica la emision; si \(t<15\), despues se aplica el salto. Como tras el ultimo croar no hay movimiento, la probabilidad final es
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
Finalmente se simplifica con el mcd.
7) Ejemplo de un solo croar.
Entre \(1\) y \(500\) hay exactamente \(95\) primos y \(405\) no primos. Entonces
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{119}{300},$$
y por complemento
$$\Pr(N)=\frac{181}{300}.$$
8) Por que bastan enteros grandes.
Cada camino solo usa factores de
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
Asi, todo denominador divide
$$500\cdot 3^{15}\cdot 2^{14}.$$
Por eso la implementacion puede llevar unicamente numeradores enteros arbitrariamente grandes.
1) Construir la criba de primalidad hasta \(500\).
2) Inicializar todos los pesos enteros con \(1\) y el denominador con \(500\).
3) En cada croar, multiplicar cada casilla por \(2\) o \(1\), y multiplicar el denominador por \(3\).
4) Si no es el ultimo croar, redistribuir a vecinos con la transicion entera y multiplicar el denominador por \(2\).
5) Sumar el vector final, reducir la fraccion y devolver el resultado exacto.
La complejidad es
$$O(NL)$$
en tiempo y
$$O(N)$$
en memoria. Con \(N=500\) y \(L=15\), es un calculo muy pequeno.
El codigo verifica
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
y que ambas suman \(1\).
Para la secuencia completa
$$\texttt{PPPPNNPPPNPPNPN}$$
la probabilidad exacta reducida es
$$\boxed{\frac{199740353}{29386561536000}}.$$
青蛙以均匀概率从 \(\{1,2,\dots,500\}\) 中某个格子出发,然后发出长度为 \(15\) 的叫声序列
$$s=\texttt{PPPPNNPPPNPPNPN}.$$
如果当前格子编号是素数,那么它发出 P 的概率是 \(2/3\),发出 N 的概率是 \(1/3\);如果当前格子不是素数,概率正好反过来。除最后一次叫声外,每次叫声之后青蛙都会跳到相邻格子:内部位置向左或向右各 \(1/2\),边界 \(1\) 和 \(500\) 只能跳向唯一邻居。要求整段序列恰好等于 \(s\) 的精确概率。
1)隐藏状态只有当前位置。
整个过程是否继续如何演化,只取决于当前格子 \(i\)。因此问题可以直接写成一个小型动态规划:在读入每一个叫声之后,维护所有 \(500\) 个位置上的概率质量。
2)先做一次素数筛。
定义
$$\pi(i)=\begin{cases}1,&\text{若 }i\text{ 为素数},\\0,&\text{否则}.\end{cases}$$
代码先用筛法求出 \(1\) 到 \(500\) 的素性表,后面每一步只需查表。
3)把公共分母整体提出来。
与其在状态中保存大量分数,不如只保存整数分子,把总分母单独维护。初始时取
$$v_0(i)=1\qquad(1\le i\le 500),\qquad D_0=500.$$
这正好表示每个格子的起始概率都是 \(1/500\)。
4)叫声步骤只会把权重乘以 \(2\) 或 \(1\)。
对第 \(t\) 次叫声,定义整数发声因子
$$e_t(i)=\begin{cases} 2,&\text{若 }s_t=P\text{ 且 }i\text{ 为素数},\\ 2,&\text{若 }s_t=N\text{ 且 }i\text{ 非素数},\\ 1,&\text{否则}. \end{cases}$$
真正的概率因子是 \(e_t(i)/3\)。于是发声后、跳跃前的整数权重为
$$u_t(i)=e_t(i)\,v_{t-1}(i),$$
同时总分母乘上 \(3\):
$$D\leftarrow 3D.$$
5)跳跃步骤也可以纯整数化。
前 \(14\) 次叫声之后都要跳一次。代码把这一整步共同抽出一个分母 \(2\),并在分子里做整数转移:
$$v_t=T(u_t).$$
若目标格子为 \(j\),则
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
边界上的系数 \(2\) 表示“强制跳跃的概率 \(1\)”在统一除以 \(2\) 的缩放下写成 \(2/2\)。然后更新
$$D\leftarrow 2D.$$
6)因此整个递推是精确分数递推。
对 \(t=1,2,\dots,15\),先做发声;若 \(t<15\),再做跳跃。最后一次叫声后不再移动,所以最终概率就是
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
最后再用 \(\gcd\) 约分。
7)单个叫声的例子最能说明缩放方式。
\(1\) 到 \(500\) 中恰好有 \(95\) 个素数,\(405\) 个非素数。因此
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{119}{300},$$
从而
$$\Pr(N)=\frac{181}{300}.$$
这正是程序里的首个检查点。
8)为什么只用整数就够了。
任意一条路径上的因子只会来自
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
所以任意路径概率的分母都整除
$$500\cdot 3^{15}\cdot 2^{14}.$$
这说明把公共分母提出来、只保存大整数分子不会损失任何精度,因此代码使用了 cpp_int。
1) 用筛法预处理 \(1\) 到 \(500\) 的素数信息。
2) 把所有初始整数权重设为 \(1\),总分母设为 \(500\)。
3) 对每个叫声,把每个位置乘上匹配因子 \(2\) 或 \(1\),并把分母乘 \(3\)。
4) 如果还不是最后一次叫声,就按整数转移式把权重分配到相邻位置,并把分母乘 \(2\)。
5) 汇总最终向量,约分,得到精确答案。
总时间复杂度为
$$O(NL),$$
空间复杂度为
$$O(N).$$
这里 \(N=500\)、\(L=15\),因此精确计算非常轻量。
程序检查
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
并确认两者相加等于 \(1\)。
对完整序列
$$\texttt{PPPPNNPPPNPPNPN}$$
得到的最简精确概率是
$$\boxed{\frac{199740353}{29386561536000}}.$$
Лягушка равновероятно стартует на одной из клеток \(\{1,2,\dots,500\}\), а затем должна издать последовательность
$$s=\texttt{PPPPNNPPPNPPNPN}$$
длины \(L=15\). Если текущая клетка простая, то звук P появляется с вероятностью \(2/3\), а N с вероятностью \(1/3\); на составной или \(1\) вероятности меняются местами. После каждого кваканья, кроме последнего, лягушка прыгает на соседнюю клетку: внутри отрезка влево или вправо с вероятностью \(1/2\), а на границах \(1\) и \(500\) переход вынужденный. Требуется точная вероятность того, что вся последовательность совпадет с \(s\).
1) Скрытое состояние - это только текущая позиция.
Будущее зависит лишь от текущей клетки \(i\), поэтому задача решается динамикой по \(500\) состояниям: после каждого звука мы обновляем распределение массы по всем клеткам.
2) Простые числа заранее просеиваются.
Обозначим
$$\pi(i)=\begin{cases}1,&\text{если }i\text{ простое},\\0,&\text{иначе}.\end{cases}$$
Код один раз строит таблицу простоты до \(500\).
3) Общий знаменатель выносится наружу.
Вместо хранения дробей в каждом состоянии программа ведет только целые числители, а общий знаменатель хранит отдельно. В начале:
$$v_0(i)=1\qquad(1\le i\le 500),\qquad D_0=500.$$
Это и есть равномерное стартовое распределение.
4) Шаг эмиссии сводится к умножению на \(2\) или на \(1\).
Для \(t\)-го символа вводится коэффициент
$$e_t(i)=\begin{cases} 2,&\text{если }s_t=P\text{ и }i\text{ простое},\\ 2,&\text{если }s_t=N\text{ и }i\text{ не простое},\\ 1,&\text{иначе}. \end{cases}$$
Реальный вероятностный множитель равен \(e_t(i)/3\). Поэтому после кваканья, но до прыжка:
$$u_t(i)=e_t(i)\,v_{t-1}(i),\qquad D\leftarrow 3D.$$
5) Переход прыжка тоже можно записать в целых числах.
После первых \(14\) звуков выполняется прыжок. Программа выносит из всего шага общий знаменатель \(2\) и переносит целые вклады:
$$v_t=T(u_t).$$
Для клетки назначения \(j\) получаем
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
Коэффициенты \(2\) на границах появляются потому, что вынужденный переход вероятности \(1\) хранится как \(2/2\) после выноса общего делителя \(2\). Затем
$$D\leftarrow 2D.$$
6) Получаем точную рекурсию.
Для \(t=1,\dots,15\) сначала применяется эмиссия, потом при \(t<15\) прыжок. После последнего звука движения нет, значит
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
В конце дробь сокращается по \(\gcd\).
7) Пример с одним звуком.
Среди чисел от \(1\) до \(500\) ровно \(95\) простых и \(405\) непростых. Поэтому
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{119}{300},$$
а значит
$$\Pr(N)=\frac{181}{300}.$$
Это первый контрольный пример в программе.
8) Почему достаточно целых чисел.
Любой путь использует только множители из набора
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
Следовательно, знаменатель любой траекторной вероятности делит
$$500\cdot 3^{15}\cdot 2^{14}.$$
Поэтому можно без потери точности хранить только числители как большие целые числа cpp_int.
1) Построить решето простоты на отрезке \(1..500\).
2) Инициализировать все целые веса единицами и взять знаменатель \(500\).
3) Для каждого символа умножить каждую клетку на \(2\) или \(1\), затем домножить знаменатель на \(3\).
4) Если символ не последний, выполнить целочисленный переход к соседям и домножить знаменатель на \(2\).
5) Просуммировать итоговый вектор, сократить дробь и вывести точный ответ.
Алгоритм работает за
$$O(NL)$$
по времени и
$$O(N)$$
по памяти. При \(N=500\) и \(L=15\) это очень маленькая вычислительная задача.
Программа проверяет
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
а также равенство \(\Pr(P)+\Pr(N)=1\).
Для полной строки
$$\texttt{PPPPNNPPPNPPNPN}$$
получается точная сокращенная вероятность
$$\boxed{\frac{199740353}{29386561536000}}.$$
تبدأ الضفدعة من أحد المربعات \(\{1,2,\dots,500\}\) باحتمال متساو، ثم نريد أن تطابق سلسلة الأصوات
$$s=\texttt{PPPPNNPPPNPPNPN}$$
وطولها \(15\). إذا كانت الضفدعة على مربع أولي فإنها تقول P باحتمال \(2/3\) وN باحتمال \(1/3\)، وإذا كانت على مربع غير أولي تنعكس الاحتمالات. بعد كل صوت ما عدا الأخير تتحرك إلى مربع مجاور: داخل المدى تتحرك يسارًا أو يمينًا باحتمال \(1/2\) لكل جهة، وعند الطرفين \(1\) و\(500\) تكون الحركة إجبارية إلى الجار الوحيد. المطلوب هو الاحتمال الدقيق لمطابقة السلسلة كاملة.
1) الحالة المخفية هي الموضع الحالي فقط.
تطور العملية يعتمد فقط على المربع الحالي \(i\)، لذلك يكفي أن نتابع توزيع الكتلة الاحتمالية على المربعات \(500\) أثناء قراءة الأصوات واحدًا بعد الآخر.
2) نحسب جدول الأولية مرة واحدة.
نعرف
$$\pi(i)=\begin{cases}1,&\text{إذا كان }i\text{ أوليًا},\\0,&\text{غير ذلك}.\end{cases}$$
يبني الكود هذا الجدول بسيف أولي حتى \(500\).
3) نخرج المقام المشترك خارج المتجه.
بدلًا من تخزين كسور كثيرة داخل الحالة، يحتفظ البرنامج ببسوط صحيحة فقط، ويضع المقام الكلي في متغير مستقل. في البداية نأخذ
$$v_0(i)=1\qquad(1\le i\le 500),\qquad D_0=500.$$
وهذا يمثل التوزيع الابتدائي المنتظم.
4) خطوة الصوت تعني الضرب في \(2\) أو \(1\).
للصوت رقم \(t\) نعرف عامل الإصدار الصحيح
$$e_t(i)=\begin{cases} 2,&\text{إذا كان }s_t=P\text{ و }i\text{ أوليًا},\\ 2,&\text{إذا كان }s_t=N\text{ و }i\text{ غير أولي},\\ 1,&\text{في غير ذلك}. \end{cases}$$
أما العامل الاحتمالي الحقيقي فهو \(e_t(i)/3\). لذلك بعد الصوت وقبل الحركة نحصل على
$$u_t(i)=e_t(i)\,v_{t-1}(i),\qquad D\leftarrow 3D.$$
5) خطوة الحركة تكتب أيضًا بصيغة صحيحة.
بعد الأصوات الأربعة عشر الأولى توجد حركة. بدل قسمة كل انتقال داخلي على \(2\) على حدة، يسحب الكود عاملًا مشتركًا \(2\) من الخطوة كلها ثم ينقل مساهمات صحيحة:
$$v_t=T(u_t).$$
وللمربع الهدف \(j\) يكون الانتقال
$$v_t(j)= \begin{cases} u_t(2), & j=1,\\ 2u_t(1)+u_t(3), & j=2,\\ u_t(j-1)+u_t(j+1), & 3\le j\le 498,\\ u_t(498)+2u_t(500), & j=499,\\ u_t(499), & j=500. \end{cases}$$
ظهور العامل \(2\) على الحدود سببه أن الحركة الإجبارية ذات الاحتمال \(1\) تخزن على صورة \(2/2\) بعد إخراج مقام مشترك يساوي \(2\). وبعدها نحدث المقام:
$$D\leftarrow 2D.$$
6) العودية النهائية دقيقة تمامًا.
لكل \(t=1,2,\dots,15\) نطبق أولًا خطوة الإصدار، ثم إذا كان \(t<15\) نطبق الحركة. وبعد الصوت الأخير لا توجد حركة إضافية، لذلك
$$\Pr(s)=\frac{\sum_{i=1}^{500}u_{15}(i)}{500\cdot 3^{15}\cdot 2^{14}}.$$
وفي النهاية يختزل الكسر باستخدام \(\gcd\).
7) مثال صوت واحد يوضح الفكرة.
بين \(1\) و\(500\) يوجد بالضبط \(95\) عددًا أوليًا و\(405\) عددًا غير أولي. لذلك
$$\Pr(P)=\frac{95\cdot\frac23+405\cdot\frac13}{500} =\frac{119}{300},$$
ومن ثم
$$\Pr(N)=\frac{181}{300}.$$
وهذه أول نقطة تحقق في البرنامج.
8) لماذا تكفي الأعداد الصحيحة الكبيرة.
كل مسار يضرب فقط عوامل من المجموعة
$$\left\{\frac23,\frac13,\frac12,1\right\}.$$
لذلك فإن مقام أي احتمال جزئي يقسم العدد
$$500\cdot 3^{15}\cdot 2^{14}.$$
ومن هنا يمكننا حفظ البسوط فقط دون أي فقدان في الدقة، ولهذا يستخدم الكود نوع cpp_int.
1) بناء جدول الأولية من \(1\) إلى \(500\).
2) تهيئة جميع الأوزان الصحيحة بالقيمة \(1\) والمقام بالقيمة \(500\).
3) عند كل صوت نضرب كل موضع في \(2\) أو \(1\)، ثم نضرب المقام في \(3\).
4) إذا لم يكن الصوت الأخير، نعيد توزيع الكتلة على الجيران وفق انتقال صحيح ثم نضرب المقام في \(2\).
5) نجمع المتجه النهائي، نختزل الكسر، ونحصل على الاحتمال الدقيق.
التكلفة الزمنية هي
$$O(NL)$$
والذاكرة
$$O(N).$$
ومع \(N=500\) و\(L=15\) يكون الحساب الدقيق سهلًا جدًا.
يفحص الكود القيمتين
$$\Pr(P)=\frac{119}{300},\qquad \Pr(N)=\frac{181}{300},$$
ويتأكد من أن مجموعهما يساوي \(1\).
أما للسلسلة الكاملة
$$\texttt{PPPPNNPPPNPPNPN}$$
فالاحتمال المختزل النهائي هو
$$\boxed{\frac{199740353}{29386561536000}}.$$