<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Prime Numbers on kenji.blog</title><link>http://kenji.blog/de/tags/prime-numbers/</link><description>Recent content in Prime Numbers on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/tags/prime-numbers/index.xml" rel="self" type="application/rss+xml"/><item><title>Die Riemannsche Vermutung und die Verteilung der Primzahlen: Die tiefe Verbindung zur modernen Kryptographie</title><link>http://kenji.blog/de/p/riemann-hypothesis-prime-distribution-cryptography/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/riemann-hypothesis-prime-distribution-cryptography/</guid><description>&lt;img src="http://kenji.blog/p/riemann-hypothesis-prime-distribution-cryptography/img/eyecatch.jpg" alt="Featured image of post Die Riemannsche Vermutung und die Verteilung der Primzahlen: Die tiefe Verbindung zur modernen Kryptographie" />&lt;h1 id="1-einleitung-das-geheimnis-des-universums-der-primzahlen-und-die-riemannsche-vermutung">1. Einleitung: Das Geheimnis des Universums der Primzahlen und die Riemannsche Vermutung
&lt;/h1>&lt;p>&amp;ldquo;Primzahlen&amp;rdquo; (Prime Numbers) sind natürliche Zahlen, die nur durch 1 und sich selbst teilbar sind, und werden auch als die &amp;ldquo;Atome&amp;rdquo; der Mathematik bezeichnet. Die Folge 2, 3, 5, 7, 11, 13&amp;hellip; scheint auf den ersten Blick unordentlich und zufällig aufzutreten. Seit der antike griechische Mathematiker Euklid bewiesen hat, dass &amp;ldquo;es unendlich viele Primzahlen gibt&amp;rdquo;, haben unzählige Mathematiker versucht, die verborgenen Gesetzmäßigkeiten in dieser Anordnung von Primzahlen zu entschlüsseln.&lt;/p>
&lt;p>Am nächsten an dieses Rätsel der Primzahlen kam die 1859 von dem deutschen Mathematiker Bernhard Riemann aufgestellte &lt;strong>&amp;ldquo;Riemannsche Vermutung&amp;rdquo; (Riemann Hypothesis)&lt;/strong> heran. Die Riemannsche Vermutung ist eines der wichtigsten und ungelösten Probleme der modernen Mathematik und als eines der Millennium-Probleme des Clay Mathematics Institute mit einem Preisgeld von einer Million Dollar dotiert.&lt;/p>
&lt;p>Auf den ersten Blick mag ein Problem der reinen Mathematik über die Verteilung von Primzahlen nichts mit unserem Alltag zu tun haben. Die Sicherheit des Internets, das die Infrastruktur der modernen Gesellschaft stützt, insbesondere &lt;strong>moderne Verschlüsselungstechnologien wie die RSA-Kryptographie und die Elliptische-Kurven-Kryptographie (ECC)&lt;/strong>, hängt jedoch stark von den Eigenschaften riesiger Primzahlen ab.&lt;/p>
&lt;p>In diesem Artikel begeben wir uns auf eine mathematische Reise von der Verteilung der Primzahlen über den Primzahlsatz und die Riemannsche Zeta-Funktion bis hin zum Kern der Riemannschen Vermutung. Wir werden extrem detailliert und tiefgreifend erklären, wie all dies mit der modernen Kryptographie verbunden ist und was mit der Welt passieren würde, wenn die Riemannsche Vermutung bewiesen werden sollte.&lt;/p>
&lt;hr>
&lt;h1 id="2-der-primzahlsatz-und-die-verteilung-der-primzahlen-die-entdeckung-von-gauss">2. Der Primzahlsatz und die Verteilung der Primzahlen: Die Entdeckung von Gauss
&lt;/h1>&lt;p>Um zu verstehen, wie Primzahlen verteilt sind, betrachteten Mathematiker die &lt;strong>Primzahlzählfunktion (Prime-counting function)&lt;/strong> $\pi(x)$, die angibt, &amp;ldquo;wie viele Primzahlen kleiner oder gleich einer bestimmten Zahl $x$ existieren&amp;rdquo;.&lt;/p>
&lt;p>Zum Beispiel:&lt;/p>
&lt;ul>
&lt;li>$\pi(10) = 4$ (2, 3, 5, 7)&lt;/li>
&lt;li>$\pi(100) = 25$&lt;/li>
&lt;li>$\pi(1000) = 168$&lt;/li>
&lt;/ul>
&lt;p>Der 15-jährige geniale Mathematiker Carl Friedrich Gauss berechnete umfangreiche Primzahltabellen und fand heraus, dass die Häufigkeit des Auftretens von Primzahlen umgekehrt proportional zum natürlichen Logarithmus $\ln x$ abnimmt. Das heißt, er vermutete, dass die Wahrscheinlichkeit, eine Primzahl in der Nähe einer bestimmten Zahl $x$ zu finden, etwa $\frac{1}{\ln x}$ beträgt.&lt;/p>
&lt;p>Dies wird mit Hilfe eines Integrals als &lt;strong>Integrallogarithmus (Logarithmic integral)&lt;/strong> $\text{Li}(x)$ ausgedrückt:&lt;/p>
$$ \text{Li}(x) = \int_{2}^{x} \frac{dt}{\ln t} $$
&lt;p>Die Vermutung von Gauss wurde später 1896 von Jacques Hadamard und Charles-Jean de La Vallée Poussin unabhängig voneinander bewiesen und als &lt;strong>Primzahlsatz (Prime Number Theorem, PNT)&lt;/strong> etabliert.&lt;/p>
$$ \lim_{x \to \infty} \frac{\pi(x)}{\text{Li}(x)} = 1 $$
&lt;p>Oder näherungsweise wie folgt ausgedrückt:&lt;/p>
$$ \pi(x) \sim \frac{x}{\ln x} $$
&lt;p>Durch diesen Satz wurde klar, dass Primzahlen makroskopisch betrachtet eine sehr glatte und vorhersehbare Verteilung aufweisen. Mikroskopisch betrachtet gibt es jedoch immer einen &amp;ldquo;Fehler&amp;rdquo; oder eine &amp;ldquo;Schwankung&amp;rdquo; zwischen $\pi(x)$ und $\text{Li}(x)$. Die wahre Natur dieser Schwankung ist das größte Rätsel, das die Riemannsche Vermutung zu lösen versucht.&lt;/p>
&lt;hr>
&lt;h1 id="3-die-riemannsche-zeta-funktion-und-das-euler-produkt">3. Die Riemannsche Zeta-Funktion und das Euler-Produkt
&lt;/h1>&lt;p>Die stärkste Waffe bei der Analyse der Verteilung von Primzahlen ist die &lt;strong>Riemannsche Zeta-Funktion (Riemann Zeta Function)&lt;/strong>. Ursprünglich war es eine unendliche Reihe, die von Leonhard Euler für reelle Zahlen $s > 1$ definiert wurde.&lt;/p>
$$ \zeta(s) = \sum_{n=1}^\infty \frac{1}{n^s} = 1 + \frac{1}{2^s} + \frac{1}{3^s} + \frac{1}{4^s} + \dots $$
&lt;p>Eine der größten Errungenschaften Eulers war der Beweis, dass diese unendliche Reihe als unendliches Produkt über alle Primzahlen $p$ dargestellt werden kann. Dies ist das &lt;strong>Euler-Produkt (Euler Product Formula)&lt;/strong>.&lt;/p>
$$ \zeta(s) = \prod_{p \text{ prime}} \frac{1}{1 - p^{-s}} = \left( \frac{1}{1 - 2^{-s}} \right) \left( \frac{1}{1 - 3^{-s}} \right) \left( \frac{1}{1 - 5^{-s}} \right) \dots $$
&lt;p>Ein intuitives Verständnis des Beweises ist, dass wenn man jeden Term auf der rechten Seite als geometrische Reihe entwickelt und diese miteinander multipliziert, aufgrund des Fundamentalsatzes der Arithmetik (jede natürliche Zahl kann eindeutig als Produkt von Primzahlen dargestellt werden) die Summe der Kehrwerte der natürlichen Zahlen auf der linken Seite vollständig rekonstruiert wird.&lt;/p>
&lt;p>&lt;strong>Diese einzige Formel wurde zur Brücke zwischen der Analysis (unendliche Reihen, stetige Funktionen) und der Zahlentheorie (Primzahlen, diskrete Zahlen).&lt;/strong> Die Untersuchung der Zeta-Funktion ist gleichbedeutend mit der Untersuchung der Verteilung der Primzahlen.&lt;/p>
&lt;hr>
&lt;h1 id="4-analytische-fortsetzung-und-die-erweiterung-auf-die-komplexe-zahlenebene">4. Analytische Fortsetzung und die Erweiterung auf die komplexe Zahlenebene
&lt;/h1>&lt;p>Riemanns Genialität lag darin, dass er die Variable $s$ von $\zeta(s)$, die Euler nur für reelle Zahlen betrachtete, auf &lt;strong>komplexe Zahlen $s = \sigma + it$ ($\sigma$ ist der Realteil, $t$ ist der Imaginärteil)&lt;/strong> erweiterte.&lt;/p>
&lt;p>Die ursprüngliche unendliche Reihe konvergiert nur für $\sigma > 1$, aber Riemann nutzte eine Methode namens &amp;ldquo;analytische Fortsetzung (Analytic Continuation)&amp;rdquo;, um die Definition so zu erweitern, dass $\zeta(s)$ auf der gesamten komplexen Zahlenebene, mit Ausnahme des Pols bei $s = 1$, sinnvoll ist.&lt;/p>
&lt;p>Er leitete außerdem eine wunderschöne Funktionalgleichung (Functional equation) ab, die die Zeta-Funktion erfüllt.&lt;/p>
$$ \zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s) $$
&lt;p>Hierbei ist $\Gamma(x)$ die Gamma-Funktion. Durch diese Gleichung können wir die Eigenschaften der linken Halbebene aus den Eigenschaften der rechten Halbebene ableiten.&lt;/p>
&lt;h3 id="nullstellen-zeros-of-the-zeta-function">Nullstellen (Zeros of the Zeta Function)
&lt;/h3>&lt;p>Komplexe Zahlen $s$, für die der Wert der Zeta-Funktion 0 wird, nennt man &amp;ldquo;Nullstellen&amp;rdquo;.
Aus der Funktionalgleichung ergibt sich, dass für negative gerade Zahlen $s$ ($-2, -4, -6, \dots$) $\sin(\pi s / 2)$ zu 0 wird, also $\zeta(s) = 0$. Diese werden &lt;strong>triviale Nullstellen (Trivial zeros)&lt;/strong> genannt.&lt;/p>
&lt;p>Was jedoch für die Verteilung der Primzahlen wichtig ist, sind die anderen Nullstellen, nämlich die &lt;strong>nicht-trivialen Nullstellen (Non-trivial zeros)&lt;/strong>, die im &amp;ldquo;kritischen Streifen (Critical strip)&amp;rdquo; von $0 \le \sigma \le 1$ liegen.&lt;/p>
&lt;hr>
&lt;h1 id="5-der-kern-der-riemannschen-vermutung-und-die-explizite-formel">5. Der Kern der Riemannschen Vermutung und die explizite Formel
&lt;/h1>&lt;p>Riemann berechnete eine kleine Anzahl von Nullstellen und stellte eine erstaunliche Vermutung auf. Dies ist die &lt;strong>Riemannsche Vermutung&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Riemannsche Vermutung (Riemann Hypothesis)&lt;/strong>
Alle nicht-trivialen Nullstellen der Riemannschen Zeta-Funktion $\zeta(s)$ liegen auf der Geraden, deren Realteil $1/2$ ist ($\text{Re}(s) = 1/2$).&lt;/p>
&lt;/blockquote>
&lt;p>Diese Gerade, deren Realteil 1/2 ist, wird als &amp;ldquo;kritische Gerade (Critical line)&amp;rdquo; bezeichnet.&lt;/p>
&lt;div class="mermaid">graph TD
A["Riemannsche Zeta-Funktion ζ(s)"] --> B["Erweiterung auf die komplexe Ebene durch analytische Fortsetzung"]
B --> C["Triviale Nullstellen (s = -2, -4, -6 ...)"]
B --> D["Nicht-triviale Nullstellen (0 &lt;= Re(s) &lt;= 1)"]
D --> E["Riemannsche Vermutung"]
E --> F["Alle nicht-trivialen Nullstellen liegen auf Re(s) = 1/2"]
F --> G["Zum Beweis des Grenzwerts des Fehlerterms in der Primzahlverteilung"]&lt;/div>
&lt;p>Warum ist die Riemannsche Vermutung so wichtig? Weil die Nullstellen der Zeta-Funktion die Verteilung der Primzahlen &lt;strong>vollständig&lt;/strong> bestimmen.&lt;/p>
&lt;p>Riemann und der spätere Mathematiker von Mangoldt leiteten eine &amp;ldquo;explizite Formel (Explicit formula)&amp;rdquo; ab, die die Verteilung der Primzahlen genau beschreibt. Unter Verwendung der Tschebyschow-Funktion $\psi(x)$ lässt sie sich wie folgt ausdrücken:&lt;/p>
$$ \psi(x) = x - \sum_{\rho} \frac{x^\rho}{\rho} - \ln(2\pi) - \frac{1}{2}\ln(1 - x^{-2}) $$
&lt;p>Hierbei ist $\rho$ die Summe über alle nicht-trivialen Nullstellen der Zeta-Funktion.
Der Hauptterm ist $x$ (der dem Primzahlsatz entspricht), und durch Addition und Subtraktion von wellenartigen Termen, die von den Nullstellen $\rho$ abhängen, wird die genaue treppenförmige Verteilung der Primzahlen wiederhergestellt. Man kann sagen, dass die nicht-trivialen Nullstellen die &amp;ldquo;Frequenzen (Wellen)&amp;rdquo; der Primzahlverteilung darstellen.&lt;/p>
&lt;p>Wenn die Riemannsche Vermutung wahr ist und der Realteil aller nicht-trivialen Nullstellen $\rho$ genau $1/2$ beträgt, dann wird der Fehlerterm des Primzahlsatzes theoretisch auf den kleinstmöglichen Bereich beschränkt sein.&lt;/p>
$$ |\pi(x) - \text{Li}(x)| \le \frac{1}{8\pi} \sqrt{x} \ln x \quad \text{for} \quad x \ge 2657 $$
&lt;p>Das bedeutet: &lt;strong>Wenn die Riemannsche Vermutung wahr ist, wird bewiesen sein, dass Primzahlen auf die &amp;ldquo;regelmäßigste und schönste&amp;rdquo; Weise verteilt sind, die wir uns vorstellen können.&lt;/strong>&lt;/p>
&lt;hr>
&lt;h1 id="6-die-untrennbare-beziehung-zwischen-der-modernen-kryptographie-und-primzahlen">6. Die untrennbare Beziehung zwischen der modernen Kryptographie und Primzahlen
&lt;/h1>&lt;p>Bisher befanden wir uns in der Welt der tiefgründigen reinen Mathematik, aber diese Eigenschaften von Primzahlen stützen die moderne digitale Gesellschaft grundlegend. Der repräsentativste Vertreter dafür ist die Public-Key-Kryptographie wie die &lt;strong>RSA-Kryptographie&lt;/strong>.&lt;/p>
&lt;p>Die Sicherheit jeglicher Kommunikation, von Kreditkartenzahlungen im Internet über das Senden von Passwörtern bis hin zu digitalen Signaturen der Blockchain, hängt von &amp;ldquo;Primzahlen&amp;rdquo; ab.&lt;/p>
&lt;h3 id="funktionsweise-der-rsa-kryptographie">Funktionsweise der RSA-Kryptographie
&lt;/h3>&lt;p>Die Sicherheit der RSA-Kryptographie beruht auf der mathematischen Tatsache, dass &amp;ldquo;die Primfaktorzerlegung großer zusammengesetzter Zahlen sehr schwierig ist&amp;rdquo; (Primfaktorzerlegungsproblem).&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Schlüsselerzeugung (Key generation)&lt;/strong>:
Zwei riesige Primzahlen $p$ und $q$ (z.B. jeweils 2048 Bit) werden zufällig ausgewählt.
Sie werden miteinander multipliziert, um $N = p \times q$ zu berechnen. Dieses $N$ wird Teil des öffentlichen Schlüssels.
Unter Verwendung der Eulerschen Phi-Funktion $\phi(N) = (p-1)(q-1)$ wird der private Schlüssel $d$ generiert.&lt;/p>
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Verschlüsselung und Entschlüsselung&lt;/strong>:
Der Klartext $M$ wird mit dem öffentlichen Schlüssel $e, N$ in einen Geheimtext $C$ umgewandelt.
&lt;/p>
$$ C \equiv M^e \pmod{N} $$
&lt;p>
Nur derjenige mit dem privaten Schlüssel $d$ kann ihn entschlüsseln.
&lt;/p>
$$ M \equiv C^d \pmod{N} $$
&lt;/li>
&lt;/ol>
&lt;div class="mermaid">graph LR
A["Klartext (Plaintext)"] --> B["Verschlüsselung mit öffentlichem Schlüssel (e, N)"]
B --> C["Geheimtext (Ciphertext)"]
C --> D["Entschlüsselung mit privatem Schlüssel (d)"]
D --> E["Ursprünglicher Klartext"]
F["Angreifer (Attacker)"] -- "Versucht die Primfaktorzerlegung von N" --> C
F -.-> G["Ohne p und q ist d unberechenbar"]&lt;/div>
&lt;p>Um die RSA-Kryptographie zu brechen, müssen aus dem riesigen $N$ die ursprünglichen Primzahlen $p$ und $q$ gefunden werden (Primfaktorzerlegung). Es wird geschätzt, dass selbst mit Supercomputern und der Verwendung derzeit gängiger Algorithmen (wie dem Zahlkörpersieb: GNFS) die Primfaktorzerlegung von Zahlen mit hunderten von Ziffern weitaus länger dauern würde als das Alter des Universums.&lt;/p>
&lt;hr>
&lt;h1 id="7-die-auswirkungen-der-riemannschen-vermutung-auf-die-kryptographie">7. Die Auswirkungen der Riemannschen Vermutung auf die Kryptographie
&lt;/h1>&lt;p>Wie kreuzen sich also die &amp;ldquo;Riemannsche Vermutung&amp;rdquo;, der Gipfel der reinen Mathematik, und die &amp;ldquo;Kryptographie&amp;rdquo;?&lt;/p>
&lt;h3 id="71-primzahlengenerierungsalgorithmen-primzahltest-und-die-erweiterte-riemannsche-vermutung-grh">7.1. Primzahlengenerierungsalgorithmen (Primzahltest) und die Erweiterte Riemannsche Vermutung (GRH)
&lt;/h3>&lt;p>Um die RSA-Kryptographie zu betreiben, müssen zuerst die riesigen Primzahlen $p$ und $q$ generiert werden. Es ist jedoch nicht einfach, zuverlässig und schnell zu bestimmen, &amp;ldquo;ob eine bestimmte Zahl eine Primzahl ist&amp;rdquo;.&lt;/p>
&lt;p>Gegenwärtig wird in der Praxis der &lt;strong>Miller-Rabin-Primzahltest (Miller-Rabin primality test)&lt;/strong>, ein probabilistischer Algorithmus, verwendet. Dieser Algorithmus ist schnell, birgt jedoch das Risiko von &amp;ldquo;Pseudoprimzahlen&amp;rdquo;, d.h., dass eine zusammengesetzte Zahl mit extrem geringer Wahrscheinlichkeit fälschlicherweise als Primzahl identifiziert wird.&lt;/p>
&lt;p>Wenn wir jedoch annehmen, dass die &lt;strong>&amp;ldquo;Erweiterte Riemannsche Vermutung&amp;rdquo; (Generalized Riemann Hypothesis, GRH)&lt;/strong>, die die Riemannsche Vermutung auf die Dirichletschen L-Funktionen erweitert, wahr ist, ändert sich die Situation dramatisch.
Wenn GRH wahr ist, wird eine obere Schranke für die Anzahl der Tests im Miller-Rabin-Verfahren mathematisch garantiert, und der probabilistische Algorithmus wird zu einem &lt;strong>&amp;ldquo;deterministischen Polynomialzeitalgorithmus&amp;rdquo; sublimiert&lt;/strong> (dies war eine wichtige Tatsache, die bereits vor der Entdeckung des AKS-Primzahltests bekannt war).&lt;/p>
&lt;p>Das heißt, die Riemannsche Vermutung (und ihre Erweiterung) spielt die Rolle einer direkten Garantie für die Generierung der Grundlage der Kryptographie: &amp;ldquo;Können riesige Primzahlen mit absoluter Zuverlässigkeit und hoher Geschwindigkeit generiert werden?&amp;rdquo;.&lt;/p>
&lt;h3 id="72-beziehung-zu-primfaktorzerlegungsalgorithmen">7.2. Beziehung zu Primfaktorzerlegungsalgorithmen
&lt;/h3>&lt;p>Bei der Bewertung der Komplexität von Algorithmen zur Entschlüsselung (wie dem Zahlkörpersieb) ist das Wissen über die Verteilung von Primzahlen ebenfalls unerlässlich. Viele Primfaktorzerlegungsalgorithmen basieren auf der Verteilung &amp;ldquo;glatter Zahlen (Smooth numbers: Zahlen, die nur kleine Primfaktoren haben)&amp;rdquo;.&lt;/p>
&lt;p>Um genau beurteilen zu können, wie oft glatte Zahlen auftreten, ist ein tiefes Verständnis der Primzahlverteilung erforderlich, und auch hier kommen Techniken der analytischen Zahlentheorie zum Einsatz, die direkt mit der Zeta-Funktion und der Riemannschen Vermutung verbunden sind. Wenn die Riemannsche Vermutung bewiesen ist und der Fehler in der Primzahlverteilung vollständig bestimmt ist, wird es möglich sein, die Leistungsgrenzen von Primfaktorzerlegungsalgorithmen genauer zu bestimmen.&lt;/p>
&lt;hr>
&lt;h1 id="8-wenn-die-riemannsche-vermutung-bewiesen-wird-wird-die-kryptographie-gebrochen">8. Wenn die Riemannsche Vermutung bewiesen wird, wird die Kryptographie gebrochen?
&lt;/h1>&lt;p>Wie eine urbane Legende wird oft erzählt: &amp;ldquo;Wenn die Riemannsche Vermutung gelöst ist, wird die RSA-Kryptographie in einem Augenblick zusammenbrechen&amp;rdquo;, aber &lt;strong>das ist mathematisch inkorrekt&lt;/strong>.&lt;/p>
&lt;p>Der Beweis der Riemannschen Vermutung selbst wird nicht sofort einen magischen Algorithmus hervorbringen, der die Primfaktorzerlegung drastisch beschleunigt. Die Riemannsche Vermutung ist nur ein Theorem über die &amp;ldquo;makroskopische Regelmäßigkeit der Verteilung&amp;rdquo; von Primzahlen und sagt uns nicht direkt, durch welche Primzahlen eine bestimmte Zahl $N$ teilbar ist (lokale Eigenschaften).&lt;/p>
&lt;p>Die Auswirkungen sind jedoch nicht null.
Es ist extrem wahrscheinlich, dass während des Prozesses des Beweises der Riemannschen Vermutung &lt;strong>&amp;ldquo;neue mathematische Werkzeuge&amp;rdquo; oder &amp;ldquo;unbekannte Analysemethoden&amp;rdquo; entdeckt werden&lt;/strong>. Die Geschichte zeigt, dass bei Beweisen wie dem Großen Fermatschen Satz oder der Poincaré-Vermutung neue Theorien entwickelt wurden, die die gesamte Mathematik maßgeblich vorangebracht haben.&lt;/p>
&lt;p>Sollten unbekannte Methoden der algebraischen Geometrie oder der nichtkommutativen Geometrie etabliert werden, die die Eigenschaften der Nullstellen der Riemannschen Zeta-Funktion vollständig manipulieren können, lässt sich nicht leugnen, dass dies zur Entdeckung eines bahnbrechenden Algorithmus zur Primfaktorzerlegung führen könnte (z.B. ein klassischer Algorithmus, der die Komplexität auf Polynomzeit reduziert). In diesem Sinne können Kryptographen ihre Augen niemals von den Entwicklungen rund um die Riemannsche Vermutung abwenden.&lt;/p>
&lt;h3 id="quantencomputer-und-shors-algorithmus">Quantencomputer und Shors Algorithmus
&lt;/h3>&lt;p>Eine direktere und realistischere Bedrohung für die Kryptographie ist nicht der Beweis der Riemannschen Vermutung, sondern &lt;strong>Quantencomputer&lt;/strong>. Der 1994 von Peter Shor veröffentlichte &amp;ldquo;Shor-Algorithmus&amp;rdquo; bewies, dass ein Quantencomputer mit ausreichender Leistung die Primfaktorzerlegung in Polynomzeit lösen kann. Dies würde die RSA-Kryptographie und die Elliptische-Kurven-Kryptographie grundlegend brechen.&lt;/p>
&lt;p>Derzeit wird weltweit der Übergang zur &amp;ldquo;Post-Quanten-Kryptographie (Post-Quantum Cryptography, PQC)&amp;rdquo; (wie gitterbasierte Kryptographie) vorangetrieben, die selbst von Quantencomputern nicht geknackt werden kann. Die von Primzahlen abhängige Kryptographie könnte in gewissem Sinne ihr goldenes Zeitalter beenden, aber der mathematische Wert der Primzahlen selbst wird niemals verloren gehen.&lt;/p>
&lt;hr>
&lt;h1 id="9-fazit-die-kreuzung-von-mathematischer-abstraktion-und-der-realen-gesellschaft">9. Fazit: Die Kreuzung von mathematischer Abstraktion und der realen Gesellschaft
&lt;/h1>&lt;div class="mermaid">graph TD
A["Erforschung der reinen Mathematik"] --> B["Aufklärung der Riemannschen Vermutung"]
B --> C["Vollständiges Verständnis der Primzahlverteilung"]
C --> D["Sprunghafte Entwicklung der Zahlentheorie und algebraischen Geometrie"]
D -.-> E["Möglichkeit neuer Primfaktorzerlegungsalgorithmen"]
E -.-> F["Aktualisierung der Sicherheitsbewertung von Kryptographie"]
A --> G["Angewandte Mathematik / Informatik"]
G --> H["Effizienzsteigerung bei Primzahltests und Schlüsselgenerierung"]
H --> F&lt;/div>
&lt;p>Die unersättliche Suche nach Primzahlen, die in der griechischen Antike begann, wurde von dem Genie Riemann in eine wunderschöne Sinfonie (die Nullstellen der Zeta-Funktion) auf der komplexen Ebene sublimiert. Erstaunlicherweise wird dieser reine Kristall der Mathematik Jahrhunderte später als der stärkste Schild zum Schutz der Sicherheit der Internetgesellschaft angewendet.&lt;/p>
&lt;p>Die Riemannsche Vermutung ist eine Existenz, die gleichzeitig die &amp;ldquo;abstrakte Schönheit&amp;rdquo; der Mathematik und ihre &amp;ldquo;erstaunliche Anwendbarkeit in der physischen Welt und der realen Gesellschaft&amp;rdquo; symbolisiert.&lt;/p>
&lt;p>Wenn dieser riesige mathematische Berg, dessen Gipfel noch niemand erreicht hat, eines Tages bezwungen wird, werden wir die Wahrheit des Universums der Primzahlen vollständig verstehen und eine neue Perspektive auf die Grundlagen der Informationsgesellschaft gewinnen. Das Studium der Kryptographie ist auch eine Reise, die die Geschichte der menschlichen Weisheit nachzeichnet.&lt;/p></description></item><item><title>Schneller Primzahltest-Algorithmus in C++ implementiert (Miller-Rabin-Methode usw.)</title><link>http://kenji.blog/de/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/cpp-fast-prime-testing-miller-rabin/</guid><description>&lt;img src="http://kenji.blog/p/cpp-fast-prime-testing-miller-rabin/img/eyecatch.jpg" alt="Featured image of post Schneller Primzahltest-Algorithmus in C++ implementiert (Miller-Rabin-Methode usw.)" />&lt;h1 id="einleitung-warum-brauchen-wir-schnelle-primzahltests">Einleitung: Warum brauchen wir schnelle Primzahltests?
&lt;/h1>&lt;p>In der Informatik, der Kryptographie und im Bereich der kompetitiven Programmierung ist die schnelle und genaue Bestimmung, &amp;ldquo;ob eine gegebene Zahl prim ist&amp;rdquo;, eine sehr wichtige und grundlegende Aufgabe. Zum Beispiel beruht die Sicherheit von Public-Key-Kryptosystemen wie RSA, die die Grundlage der heutigen Internetsicherheit bilden, auf der Erzeugung riesiger Primzahlen und der Schwierigkeit ihrer Multiplikation (bzw. der Schwierigkeit der Primfaktorzerlegung). Daher ist es keine Übertreibung zu sagen, dass die Technologie zur sofortigen Identifizierung riesiger Primzahlen das Rückgrat unserer digitalen Gesellschaft bildet.&lt;/p>
&lt;p>Auch beim kompetitiven Programmieren (wie AtCoder oder Codeforces) sind Primzahltests ein häufiges Thema. Wenn für riesige Eingaben mit Beschränkungen wie $N \le 10^{18}$ Zehntausende von Primzahltests in weniger als 1 Sekunde durchgeführt werden müssen, werden traditionelle, naive Algorithmen das Zeitlimit (Time Limit Exceeded: TLE) niemals einhalten.&lt;/p>
&lt;p>In diesem Artikel werden wir von naiven Primzahltests über den &amp;ldquo;Fermat-Test&amp;rdquo;, einen probabilistischen Algorithmus, bis hin zum &amp;ldquo;Miller-Rabin-Primzahltest&amp;rdquo;, einem Hochgeschwindigkeitsalgorithmus auf praktisch höchstem Niveau, der die Schwächen des Fermat-Tests überwindet, alles ausführlich erklären – vom mathematischen Hintergrund bis hin zu einer hochgradig optimierten Implementierung in C++. Insbesondere für 64-Bit-Ganzzahlen ($N &lt; 2^{64}$) werden wir nicht nur auf probabilistische Tests eingehen, sondern auch &amp;ldquo;100% sichere Primzahltests (deterministische Tests)&amp;rdquo; detailliert beschreiben und einen C++-Quellcode bereitstellen, der direkt in der Praxis verwendet werden kann.&lt;/p>
&lt;hr>
&lt;h1 id="1-grundlagen-der-primzahltests-und-probedivision-trial-division">1. Grundlagen der Primzahltests und Probedivision (Trial Division)
&lt;/h1>&lt;p>Eine Primzahl (Prime number) ist eine natürliche Zahl größer als 1, die außer 1 und sich selbst keine positiven Teiler hat. Folgt man strikt der Definition einer Primzahl, kann man feststellen, ob eine ganze Zahl $N$ eine Primzahl ist, indem man $N$ durch alle ganzen Zahlen von $2$ bis $N-1$ dividiert. Wenn sie durch keine dieser Zahlen teilbar ist, ist sie prim, andernfalls ist sie eine zusammengesetzte Zahl (keine Primzahl).&lt;/p>
&lt;p>Diese Methode hat jedoch eine Zeitkomplexität von $O(N)$. Wenn $N$ eine riesige Zahl wie $10^{18}$ ist, würde selbst ein moderner Computer enorm viel Zeit für die Berechnung benötigen.&lt;/p>
&lt;h2 id="optimierung-der-probedivision-suche-bis-sqrtn">Optimierung der Probedivision: Suche bis $\sqrt{N}$
&lt;/h2>&lt;p>Wenn eine zusammengesetzte Zahl $N$ als $a \times b = N$ (mit $a \le b$) dargestellt wird, gilt immer $a \le \sqrt{N}$. Daher ist es nicht notwendig, die Schleife für den Primzahltest bis $N-1$ laufen zu lassen; es reicht aus, bis $\sqrt{N}$ zu prüfen.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Primzahltest durch Probedivision (O(sqrt(N)))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_trial_division&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Nur ungerade Zahlen ab 3 prüfen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Die Zeitkomplexität dieses Algorithmus beträgt $O(\sqrt{N})$. Für $N \le 10^{12}$ kann die Berechnung im Bruchteil einer Sekunde durchgeführt werden. Bei $N \approx 10^{18}$ beträgt die Anzahl der Schleifendurchläufe jedoch etwa $10^9$, was selbst in C++ Hunderte von Millisekunden bis zu mehreren Sekunden dauert und somit für mehrfache Tests ungeeignet ist.&lt;/p>
&lt;hr>
&lt;h1 id="2-fermat-test-der-beginn-der-probabilistischen-primzahltests">2. Fermat-Test: Der Beginn der probabilistischen Primzahltests
&lt;/h1>&lt;p>Um die Grenzen der Probedivision zu überwinden, wurden &amp;ldquo;probabilistische Algorithmen (Probabilistic Algorithm)&amp;rdquo; entwickelt, die zahlentheoretische Theoreme nutzen. Ein typisches Beispiel ist der &amp;ldquo;Fermat-Primzahltest (Fermat Primality Test)&amp;rdquo;, der den kleinen Fermatschen Satz anwendet.&lt;/p>
&lt;h2 id="kleiner-fermatscher-satz-fermats-little-theorem">Kleiner Fermatscher Satz (Fermat&amp;rsquo;s Little Theorem)
&lt;/h2>&lt;p>Dieser von Pierre de Fermat entdeckte Satz besagt Folgendes:&lt;/p>
&lt;blockquote>
&lt;p>Für jede Primzahl $p$ und jede beliebige ganze Zahl $a$, die teilerfremd zu $p$ ist (kein Vielfaches von $p$), gilt die folgende Kongruenz:
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>Die Kontraposition dieses Satzes besagt: &amp;ldquo;Wenn für eine ganze Zahl $N$ und eine zu $N$ teilerfremde ganze Zahl $a$ gilt $a^{N-1} \not\equiv 1 \pmod N$, dann ist $N$ definitiv eine zusammengesetzte Zahl.&amp;rdquo; Der Fermat-Test nutzt diese Eigenschaft, indem er für die zu testende Zahl $N$ eine zufällige Basis (base) $a$ wählt und überprüft, ob $a^{N-1} \pmod N$ den Wert $1$ ergibt.&lt;/p>
&lt;h2 id="schnelle-modulare-exponentiation-binäre-exponentiation">Schnelle modulare Exponentiation (Binäre Exponentiation)
&lt;/h2>&lt;p>Um den Fermat-Test durchzuführen, müssen wir die riesige Potenz $a^{N-1} \pmod N$ schnell berechnen. Hierfür wird die &amp;ldquo;Binäre Exponentiation (Modular Exponentiation / Binary Exponentiation)&amp;rdquo; verwendet. Die Zeitkomplexität beträgt $O(\log N)$ und ist damit extrem schnell.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Berechnung von a^b mod m mittels binärer Exponentiation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>Hier verwenden wir zur Vermeidung von Überläufen &lt;code>__int128_t&lt;/code> (eine 128-Bit-Ganzzahl), eine Erweiterung von GCC/Clang, um Zwischenprodukte zu speichern.&lt;/li>
&lt;/ul>
&lt;h2 id="pseudoprimzahlen-und-carmichael-zahlen-carmichael-numbers">Pseudoprimzahlen und Carmichael-Zahlen (Carmichael Numbers)
&lt;/h2>&lt;p>Der Fermat-Test ist sehr mächtig, hat jedoch eine fatale Schwäche. Es gibt Zahlen $N$, die zusammengesetzt sind, für die aber dennoch $a^{N-1} \equiv 1 \pmod N$ für alle $a$ (die zu $N$ teilerfremd sind) gilt.&lt;/p>
&lt;p>Solche Zahlen werden &amp;ldquo;absolute Pseudoprimzahlen&amp;rdquo; oder &amp;ldquo;Carmichael-Zahlen&amp;rdquo; genannt. Die kleinste Carmichael-Zahl ist $561 = 3 \times 11 \times 17$.
Aufgrund der Existenz von Carmichael-Zahlen kann der Fermat-Test allein keine 100%ige Sicherheit (deterministischer Test) bieten. Egal wie viele verschiedene $a$ man probiert, Zahlen wie $561$ werden immer so tun, als wären sie Primzahlen.&lt;/p>
&lt;hr>
&lt;h1 id="3-miller-rabin-primzahltest-miller-rabin-primality-test">3. Miller-Rabin-Primzahltest (Miller-Rabin Primality Test)
&lt;/h1>&lt;p>Die Schwäche des Fermat-Tests (die Existenz von Carmichael-Zahlen) wurde von Gary L. Miller und Michael O. Rabin mit dem &amp;ldquo;Miller-Rabin-Primzahltest&amp;rdquo; brillant überwunden.
Heute wird er am häufigsten als praktischer, schneller Primzahltest-Algorithmus in internen Bibliotheken verschiedener Programmiersprachen und bei der Schlüsselgenerierung in Kryptosystemen verwendet.&lt;/p>
&lt;h2 id="mathematisches-prinzip">Mathematisches Prinzip
&lt;/h2>&lt;p>Der Miller-Rabin-Algorithmus nutzt zusätzlich zum kleinen Fermatschen Satz die Eigenschaft, dass &amp;ldquo;in einem Restklassenkörper modulo einer Primzahl ($\mathbb{Z}/p\mathbb{Z}$) die einzigen Lösungen von $x^2 \equiv 1 \pmod p$ die Werte $x \equiv 1$ oder $x \equiv -1$ sind&amp;rdquo; (bei einer zusammengesetzten Zahl als Modul können auch andere nicht-triviale Quadratwurzeln existieren).&lt;/p>
&lt;p>Zieht man $1$ von einer zu testenden ungeraden Zahl $N$ ab, ist $N-1$ immer gerade. Daher teilen wir $N-1$ so oft wie möglich durch $2$ und schreiben es in der folgenden Form:
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
(Wobei $d$ ungerade ist und $s \ge 1$)&lt;/p>
&lt;p>Für eine beliebige Basis $a$ ($1 &lt; a &lt; N-1$) prüfen wir gemäß dem kleinen Fermatschen Satz, ob $a^{N-1} \equiv 1 \pmod N$ gilt, führen diese Berechnung jedoch schrittweise durch.
Konkret wiederholen wir die Quadrierung in der Reihenfolge $a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$.&lt;/p>
&lt;p>Die Bedingung dafür, dass der Miller-Rabin-Test $N$ als &amp;ldquo;prim (oder mit hoher Wahrscheinlichkeit prim)&amp;rdquo; einstuft, ist, dass &lt;strong>eine&lt;/strong> der folgenden Aussagen wahr ist:&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>Es gibt ein $r$ ($0 \le r &lt; s$), für das gilt: $a^{d \cdot 2^r} \equiv -1 \pmod N$.
&lt;ul>
&lt;li>In C++ Modulo-Arithmetik wird $-1 \pmod N$ als $N-1$ dargestellt.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Wenn $N$ eine Primzahl ist, ist diese Bedingung für jedes beliebige $a$ immer erfüllt. Umgekehrt ist mathematisch bewiesen, dass, wenn $N$ eine zusammengesetzte Zahl ist und ein zufälliges $a$ gewählt wird, die Wahrscheinlichkeit, dass diese Bedingung erfüllt ist (die Wahrscheinlichkeit, getäuscht zu werden), höchstens $\frac{1}{4}$ beträgt.
Führt man $k$ unabhängige Tests durch, sinkt die Wahrscheinlichkeit eines falschen Ergebnisses auf höchstens $\left(\frac{1}{4}\right)^k$, was für die Praxis als null betrachtet werden kann. Es gibt keine &amp;ldquo;absolut täuschenden&amp;rdquo; Zahlen wie Carmichael-Zahlen mehr.&lt;/p>
&lt;h2 id="ablauf-des-miller-rabin-algorithmus-mermaid-flussdiagramm">Ablauf des Miller-Rabin-Algorithmus (Mermaid-Flussdiagramm)
&lt;/h2>&lt;p>Das folgende Diagramm zeigt den logischen Ablauf eines einzelnen Miller-Rabin-Primzahltests (für eine Basis $a$).&lt;/p>
&lt;div class="mermaid">graph TD
Start["Teststart (Eingabe: N, a)"] --> CalcDS["Berechne ungerades d und s für N-1 = d * 2^s"]
CalcDS --> CalcX["Berechne x = a^d mod N"]
CalcX --> CheckX1{"x == 1 oder x == N-1 ?"}
CheckX1 -- "Yes" --> ReturnTrue["Möglicherweise prim (Probably Prime)"]
CheckX1 -- "No" --> LoopStart["Schleifenstart von r = 1 bis s-1"]
LoopStart --> LoopCondition{"r &lt; s ?"}
LoopCondition -- "No" --> ReturnFalse["Definitiv zusammengesetzt (Composite)"]
LoopCondition -- "Yes" --> SquareX["Berechne x = (x * x) mod N"]
SquareX --> CheckXMinus1{"x == N - 1 ?"}
CheckXMinus1 -- "Yes" --> ReturnTrue
CheckXMinus1 -- "No" --> CheckXOne{"x == 1 ?"}
CheckXOne -- "Yes" --> ReturnFalse
CheckXOne -- "No" --> LoopNext["Erhöhe r um 1 und weiter"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-deterministischer-test-für-64-bit-ganzzahlen">4. Deterministischer Test für 64-Bit-Ganzzahlen
&lt;/h1>&lt;p>Der Miller-Rabin-Primzahltest ist ursprünglich ein &amp;ldquo;probabilistischer&amp;rdquo; Algorithmus, aber wenn die Obergrenze von $N$ feststeht, kann man &amp;ldquo;100% deterministisch&amp;rdquo; auf eine Primzahl testen, indem man bestimmte Mengen von $a$ (Basen) ausprobiert.
Dies wird als &lt;strong>Deterministischer Miller-Rabin-Test (Deterministic Miller-Rabin Test)&lt;/strong> bezeichnet.&lt;/p>
&lt;p>Durch Forschungen von Jim Sinclair und anderen wurde herausgefunden, dass für alle ganzen Zahlen $N &lt; 2^{64}$ (ca. $1.8 \times 10^{19}$) ein vollständig deterministischer Test möglich ist, indem man die folgenden $7$ Primzahlen als Basis $a$ wählt und testet.&lt;/p>
&lt;p>&lt;strong>Liste der zu testenden Basen $a$:&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>Alternativ kann man eine andere bekannte Menge von $12$ Primzahlen verwenden, um Zahlen bis $N &lt; 2^{64}$ perfekt zu testen.
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>Um die Einfachheit und Zuverlässigkeit des Algorithmus zu erhöhen, werden wir diesmal die Methode mit den $12$ Primzahlen (oder die optimierteren $7$ Basen) übernehmen. Bei der C++-Implementierung optimieren wir den Prozess, indem wir die Anzahl der Tests basierend auf Größenbereichen durch Verzweigungen minimieren.&lt;/p>
&lt;hr>
&lt;h1 id="5-hochgradig-optimierte-implementierung-in-c-highly-optimized-c-implementation">5. Hochgradig optimierte Implementierung in C++ (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>Nun fassen wir die mathematische Theorie und den Entwurf des Algorithmus zusammen und präsentieren den Implementierungscode für eine der stärksten Miller-Rabin-Primzahltest-Funktionen im modernen C++.&lt;/p>
&lt;h2 id="punkte-zur-implementierung">Punkte zur Implementierung
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Vermeidung von Überläufen bei der 64-Bit-Ganzzahl-Multiplikation:&lt;/strong>
Wenn $N \approx 10^{18}$, kann $x \times x$ bei der Modulo-Multiplikation bis zu $10^{36}$ erreichen und den Maximalwert einer regulären 64-Bit-Ganzzahl (&lt;code>uint64_t&lt;/code> oder &lt;code>long long&lt;/code>) von $1.8 \times 10^{19}$ leicht überlaufen lassen.
Um dieses Problem zu lösen, verwenden wir &lt;code>__int128_t&lt;/code> (oder &lt;code>unsigned __int128&lt;/code>), einen Erweiterungstyp von GCC und Clang, führen die Berechnung mit 128-Bit-Genauigkeit durch und wenden dann Modulo an. So sind schnelle modulare Multiplikationen ohne komplexe Algorithmen möglich.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Auswahl deterministischer Basen:&lt;/strong>
Ist der Wert von $N$ klein, optimieren wir dies, sodass nur wenige Basen getestet werden müssen.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="vollständiger-c-quellcode">Vollständiger C++ Quellcode
&lt;/h2>&lt;p>Hier ist der fertige, praxistaugliche Quellcode. Dieser Code kann für Umgebungen wie kompetitives Programmieren direkt kopiert und verwendet werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;initializer_list&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Schnelle Berechnung von (a * b) mod m mit 128-Bit-Ganzzahlen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Berechnung von (base^exp) mod m mittels binärer Exponentiation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Deterministischer Miller-Rabin-Primzahltest für 64-Bit-Ganzzahlen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Vorabprüfung von Randwerten und kleinen Primzahlen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Zerlegung in die Form n-1 = d * 2^s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Liste der für den Test verwendeten Basen (bases)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Optimierung zur Minimierung der Anzahl der zu testenden Basen je nach Größe von N
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">4759123141ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1122004669633ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1662803&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 7 Basen, die für alle Zahlen N &amp;lt; 2^64 deterministisch sind
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9375&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">28178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">450775&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9780504&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1795265022&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Test für jede Basis durchführen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">a&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Ist a ein Vielfaches von n, ist der Test nicht anwendbar, aber n keine Primzahl
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Erste Bedingung erfüllt, zur nächsten Basis
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Schleife s-1 mal ausführen (x = x^2 mod n)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Zweite Bedingung erfüllt, möglicherweise prim
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Wenn keine Bedingung erfüllt ist, ist sie definitiv zusammengesetzt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composite&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Wenn alle Basen die Bedingungen erfüllen, ist die Zahl definitiv eine Primzahl
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Testbeispiele
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">test_cases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Bekannte Primzahl
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">998244353&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Bekannte Primzahl
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Primzahl im Bereich von 10^18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Zusammengesetzte Zahl (10^18 + 7)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">561&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Carmichael-Zahl (zusammengesetzt)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">18446744073709551557ULL&lt;/span> &lt;span class="c1">// Eine der größten Primzahlen nahe 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">n&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s">&amp;#34;Prime&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s">&amp;#34;Composite&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-zeitkomplexität-des-algorithmus-und-leistungsbewertung">6. Zeitkomplexität des Algorithmus und Leistungsbewertung
&lt;/h1>&lt;p>Lassen Sie uns die Leistung des implementierten Algorithmus analysieren.&lt;/p>
&lt;h2 id="zeitkomplexität-time-complexity">Zeitkomplexität (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Probedivision:&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>Fermat-Test:&lt;/strong> Exponentiation $O(\log N) \times k$ (wobei $k$ die Anzahl der Versuche ist)&lt;/li>
&lt;li>&lt;strong>Miller-Rabin-Test:&lt;/strong> Exponentiation und Schleife $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>In einer 64-Bit-Umgebung ($N \le 2^{64}$) prüft die oben beschriebene deterministische Miller-Rabin-Methode maximal $7$ Basen. Daher kann $k \le 7$ als Konstante betrachtet werden, und die gesamte Zeitkomplexität ist streng genommen $O(\log N)$.
Selbst im größten Fall ($N \approx 10^{19}$) passt die Anzahl der Ausführungsschritte in höchstens $7 \times 64 = 448$ elementare Operationen, und die Ausführungszeit beträgt wenige Mikrosekunden ($10^{-6}$ Sekunden) oder weniger. Im Vergleich zur Zeitkomplexität der Probedivision von $O(\sqrt{N})$ (Schleifendurchläufe $\approx 4 \times 10^9$) wurde eine &lt;strong>Beschleunigung um das Millionenfache&lt;/strong> erreicht.&lt;/p>
&lt;h2 id="weitere-optimierung-montgomery-multiplikation-montgomery-multiplication">Weitere Optimierung: Montgomery-Multiplikation (Montgomery Multiplication)
&lt;/h2>&lt;p>Die Implementierung in diesem Artikel verwendet den 128-Bit-Erweiterungstyp &lt;code>__int128_t&lt;/code> für die Division (Modulo-Operation &lt;code>%&lt;/code>). Selbst auf modernen CPUs ist die Ganzzahldivision (DIV-Befehl) ein teurer Befehl, der dutzende von Zyklen erfordert, weit mehr als Addition oder Multiplikation.&lt;/p>
&lt;p>Bibliotheksersteller und wettbewerbsorientierte Programmierer, die nach extremer Optimierung streben, verwenden oft eine Technik namens &lt;strong>Montgomery-Multiplikation (Montgomery Multiplication)&lt;/strong>. Die Montgomery-Multiplikation ist ein verblüffender Algorithmus, der Zahlen in einen speziellen &amp;ldquo;Montgomery-Raum&amp;rdquo; abbildet und so hochgradig kostspielige Modulo-Operationen (Division) durch &amp;ldquo;nur Bitverschiebungen und Multiplikationen&amp;rdquo; ersetzt.
Durch die Integration dieser Technik in die modulare Multiplikation des Miller-Rabin-Tests kann die Ausführungsgeschwindigkeit weiter verdoppelt oder verdreifacht werden. Da dies ein sehr tiefes Thema ist, werde ich es in einem separaten Artikel ausführlich behandeln.&lt;/p>
&lt;hr>
&lt;h1 id="7-zusammenfassung">7. Zusammenfassung
&lt;/h1>&lt;p>In diesem Artikel haben wir von den Grundlagen der Primzahltests bis hin zu fortgeschrittenen Themen alles behandelt.
Fassen wir die wichtigsten Punkte zusammen:&lt;/p>
&lt;ol>
&lt;li>Die &lt;strong>Probedivision&lt;/strong> ist zuverlässig, hat aber eine Zeitkomplexität von $O(\sqrt{N})$, was sie für $N > 10^{12}$ unpraktisch macht.&lt;/li>
&lt;li>Der &lt;strong>Fermat-Test&lt;/strong> ist mit $O(\log N)$ sehr schnell, hat aber die fatale Schwäche, von absoluten Pseudoprimzahlen wie Carmichael-Zahlen getäuscht zu werden.&lt;/li>
&lt;li>Der &lt;strong>Miller-Rabin-Primzahltest&lt;/strong> ist ein praktischer, extrem starker Algorithmus, der die Schwäche des Fermat-Tests beseitigt.&lt;/li>
&lt;li>Bei der C++ Implementierung kann man den Erweiterungstyp &lt;code>__int128_t&lt;/code> nutzen, um Überläufe bei der Multiplikation von 64-Bit-Ganzzahlen sicher zu handhaben.&lt;/li>
&lt;li>Im Bereich der 64-Bit-Ganzzahlen ($N &lt; 2^{64}$) können Primzahltests &lt;strong>deterministisch (100% genau)&lt;/strong> durchgeführt werden, anstatt nur probabilistisch, indem $7$ oder $12$ spezifische Primzahlen als Basis gewählt werden.&lt;/li>
&lt;/ol>
&lt;p>Schnelle Primzahltests sind eine unverzichtbare Technologie beim Rechnen mit riesigen Zahlen. Der hier vorgestellte C++-Quellcode für den Miller-Rabin-Test ist robust und kann direkt in der Praxis eingesetzt werden. Bitte probieren Sie ihn in Ihren eigenen Projekten oder bei Algorithmen-Wettbewerben aus!&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["Probedivision (O(√N))"] --> Fermat["Fermat-Test (O(log N), hat Schwächen)"]
Fermat --> MillerRabin["Miller-Rabin-Test (O(log N), deterministisch möglich)"]
MillerRabin --> Montgomery["+ Montgomery-Multiplikation (Schneller um einen konstanten Faktor)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>Die Welt der Algorithmen, in der sich Programmierung und Mathematik kreuzen, ist unglaublich schön und tiefgründig. Ich hoffe, dieser Artikel hilft Ihnen beim weiteren Lernen.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Reference:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Pomerance, C., Selfridge, J. L., &amp;amp; Wagstaff, S. S. (1980). The pseudoprimes to 25.10^9. Mathematics of Computation.&lt;/em>&lt;/li>
&lt;li>&lt;em>Sinclair, J. (2011). Deterministic Miller-Rabin primality testing.&lt;/em>&lt;/li>
&lt;/ul></description></item><item><title>【Mathematik-Erklärung】Wie RSA-Verschlüsselung funktioniert, verständlich für Schüler erklärt</title><link>http://kenji.blog/de/p/rsa-encryption-math-explained-for-beginners/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/rsa-encryption-math-explained-for-beginners/</guid><description>&lt;img src="http://kenji.blog/p/rsa-encryption-math-explained-for-beginners/img/eyecatch.jpg" alt="Featured image of post 【Mathematik-Erklärung】Wie RSA-Verschlüsselung funktioniert, verständlich für Schüler erklärt" />&lt;p>Eine der Technologien, die die Sicherheit der Internetgesellschaft grundlegend unterstützt, ist die „RSA-Verschlüsselung“. Viele der Kommunikationen, die wir jeden Tag beiläufig nutzen, wie Kreditkartenzahlungen beim Online-Shopping, der Austausch mit Freunden auf sozialen Netzwerken und das Senden und Empfangen vertraulicher Unternehmensinformationen, werden durch diese RSA-Verschlüsselung und ihre Nachfolgetechnologien geschützt.&lt;/p>
&lt;p>Wenn man jedoch das Wort „Verschlüsselung“ hört, stellt man sich vielleicht komplexe Chiffriermaschinen wie in Spionagefilmen vor oder extrem fortgeschrittene Mathematik, die nur einige wenige Genies verstehen können. Es stimmt zwar, dass die moderne Kryptographie auf fortgeschrittener Mathematik basiert, aber &lt;strong>die grundlegende Funktionsweise der RSA-Verschlüsselung kann mit dem Wissen aus der Schulmathematik (Eigenschaften von ganzen Zahlen, Primzahlen, Kongruenzen usw.) gut verstanden werden&lt;/strong>.&lt;/p>
&lt;p>In diesem Artikel erklären wir ausgehend vom Wissen der Schulmathematik Schritt für Schritt, nach welchen mathematischen Prinzipien die RSA-Verschlüsselung funktioniert und warum sie so schwer zu knacken ist. Wir werden es anhand konkreter Beispiele sorgfältig erklären, so dass auch diejenigen es verstehen können, die in Mathematik vielleicht nicht so stark sind.&lt;/p>
&lt;hr>
&lt;h2 id="1-symmetrische-verschlüsselung-und-asymmetrische-verschlüsselung-public-key-verschlüsselung">1. Symmetrische Verschlüsselung und asymmetrische Verschlüsselung (Public-Key-Verschlüsselung)
&lt;/h2>&lt;p>Bevor wir uns mit den mathematischen Mechanismen der RSA-Verschlüsselung befassen, lassen Sie uns zunächst die Grundkonzepte der Kryptographie klären. Verschlüsselungsverfahren lassen sich grob in zwei Arten unterteilen: die „symmetrische Verschlüsselung“ und die „asymmetrische Verschlüsselung“ (Public-Key-Kryptographie).&lt;/p>
&lt;h3 id="11-die-grenzen-der-symmetrischen-verschlüsselung">1.1 Die Grenzen der symmetrischen Verschlüsselung
&lt;/h3>&lt;p>Viele der seit langem verwendeten Verschlüsselungen sind sogenannte „symmetrische Verschlüsselungsverfahren“. Bei dieser Methode wird &lt;strong>für die „Verschlüsselung“ (die Umwandlung einer Nachricht in einen geheimen Geheimtext) und die „Entschlüsselung“ (die Wiederherstellung der ursprünglichen Nachricht aus dem Geheimtext) derselbe Schlüssel verwendet&lt;/strong>.&lt;/p>
&lt;p>Nehmen wir zum Beispiel an, Alice möchte Bob einen geheimen Brief schicken. Alice legt den Brief in eine Kiste und verschließt diese mit einem Vorhängeschloss (dem symmetrischen Schlüssel). Damit Bob diese Kiste öffnen kann, muss er denselben Schlüssel besitzen, den auch Alice benutzt hat.&lt;/p>
&lt;p>Dieses System hat ein großes Problem: das „Schlüsselaustauschproblem“. Wenn Alice und Bob, die weit voneinander entfernt sind, zum ersten Mal kommunizieren, wie können sie dann einen Schlüssel teilen, ohne abgehört zu werden? Wenn der Schlüssel auf dem Postweg von einem Dritten gestohlen wird, ist die gesamte nachfolgende verschlüsselte Kommunikation für diesen einsehbar.&lt;/p>
&lt;h3 id="12-die-bahnbrechende-erfindung-der-asymmetrischen-verschlüsselung">1.2 Die bahnbrechende Erfindung der „asymmetrischen Verschlüsselung“
&lt;/h3>&lt;p>Um dieses Schlüsselaustauschproblem zu lösen, wurde die „asymmetrische Verschlüsselung“ erfunden. Die RSA-Verschlüsselung ist eine Form davon.&lt;/p>
&lt;p>Bei der asymmetrischen Verschlüsselung werden &lt;strong>zwei unterschiedliche Schlüssel verwendet: ein „Schlüssel zum Verschlüsseln (öffentlicher Schlüssel)“ und ein „Schlüssel zum Entschlüsseln (privater Schlüssel)“&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>Der Empfänger Bob erstellt ein Paar aus „öffentlichem Schlüssel“ und „privatem Schlüssel“.&lt;/li>
&lt;li>Bob veröffentlicht den „öffentlichen Schlüssel“ für die ganze Welt (es spielt keine Rolle, wer ihn bekommt).&lt;/li>
&lt;li>Die Senderin Alice verschlüsselt ihre Nachricht mit Bobs „öffentlichem Schlüssel“ und sendet sie ab.&lt;/li>
&lt;li>Die verschlüsselte Nachricht kann nur mit dem „privaten Schlüssel“, den nur Bob besitzt, entschlüsselt werden.&lt;/li>
&lt;/ol>
&lt;p>Um dies mit einem Vorhängeschloss zu vergleichen: Bob stellt viele „offene Vorhängeschlösser (öffentliche Schlüssel)“ her und verteilt sie in der ganzen Welt. Alice legt eine Nachricht für Bob in eine Kiste und verschließt sie mit einem gefundenen Vorhängeschloss von Bob. Sobald das Vorhängeschloss geschlossen ist, kann es nur noch mit dem „Generalschlüssel (privater Schlüssel)“ geöffnet werden, den Bob besitzt. Selbst wenn jemand unterwegs die Kiste stiehlt, kann er sie ohne den Generalschlüssel nicht öffnen.&lt;/p>
&lt;div class="mermaid">graph TD
A["Alice (Senderin)"] --> B["Klartext (Nachricht)"]
B --> C["Verschlüsselungsprozess"]
D["Bobs öffentlicher Schlüssel (für jeden zugänglich)"] --> C
C --> E["Versand über das Internet: Geheimtext"]
E --> F["Entschlüsselungsprozess"]
G["Bobs privater Schlüssel (nur Bob hat ihn)"] --> F
F --> H["Wiederhergestellter Klartext (Nachricht)"]
H --> I["Bob (Empfänger)"]&lt;/div>
&lt;p>Um dieses revolutionäre System zu realisieren, ist eine Art &lt;strong>„Einwegfunktion (ein mathematisches Einbahnstraßen-Rätsel)“&lt;/strong> erforderlich, die es erlaubt, „mit dem öffentlichen Schlüssel leicht zu verschlüsseln, aber ohne den privaten Schlüssel absolut nicht zu entschlüsseln“. Als Bestandteil dieses Rätsels richtete man die Aufmerksamkeit auf etwas, das wir gut kennen: „Primzahlen“.&lt;/p>
&lt;hr>
&lt;h2 id="2-mathematische-grundlagen-der-rsa-verschlüsselung-teil-1-primzahlen-und-primfaktorzerlegung">2. Mathematische Grundlagen der RSA-Verschlüsselung Teil 1: Primzahlen und Primfaktorzerlegung
&lt;/h2>&lt;p>Die Sicherheit der RSA-Verschlüsselung basiert auf der mathematischen Tatsache, dass &lt;strong>„die Primfaktorzerlegung riesiger Zahlen extrem schwierig ist“&lt;/strong>.&lt;/p>
&lt;h3 id="21-was-sind-primzahlen">2.1 Was sind Primzahlen?
&lt;/h3>&lt;p>Eine Primzahl ist „eine natürliche Zahl größer als 1, die nur durch 1 und durch sich selbst teilbar ist“.
Beispiel: $2, 3, 5, 7, 11, 13, 17, 19, 23...$&lt;/p>
&lt;p>Primzahlen sind wie die „Atome“ aller ganzen Zahlen. Jede natürliche Zahl kann in eine Multiplikation von Primzahlen zerlegt werden. Dies nennt man &lt;strong>Primfaktorzerlegung&lt;/strong>. Dass sich eine Zahl, abgesehen von der Reihenfolge der Faktoren, auf genau eine Weise in Primfaktoren zerlegen lässt (wie z. B. $60 = 2^2 \times 3 \times 5$), ist als der „Fundamentalsatz der Arithmetik“ bekannt.&lt;/p>
&lt;h3 id="22-die-schwierigkeit-der-primfaktorzerlegung-einwegfunktion">2.2 Die Schwierigkeit der Primfaktorzerlegung (Einwegfunktion)
&lt;/h3>&lt;p>Wichtig ist hier die Asymmetrie: &lt;strong>„Multiplizieren ist einfach, aber die Primfaktorzerlegung ist schwierig.“&lt;/strong>&lt;/p>
&lt;p>Versuchen Sie beispielsweise, die Multiplikation der folgenden zwei Primzahlen im Kopf zu berechnen:
$11 \times 13 = ?$
Das ist einfach. Die Antwort ist $143$.&lt;/p>
&lt;p>Aber wie steht es mit der nächsten Zahl?
Bitte zerlegen Sie $323$ in ihre Primfaktoren.
Wie sieht es aus? Das dürfte ein wenig Zeit in Anspruch nehmen. (Die Antwort ist $17 \times 19$).&lt;/p>
&lt;p>Wenn die Zahlen klein sind, kann ein Mensch sie irgendwie noch berechnen, aber wenn die Zahlen größer werden, wird die Berechnung selbst für Computer explosiv schwierig. Die derzeit vorherrschende RSA-Verschlüsselung verwendet eine Zahl $N = p \times q$, die das Produkt aus zwei unfassbar riesigen Primzahlen $p$ und $q$ von 2048 Bit (etwa 600 Dezimalstellen) ist.&lt;/p>
&lt;p>Wenn zwei riesige Primzahlen $p$ und $q$ gegeben sind, ist die Berechnung von $N$ für einen Computer eine Sache von Millisekunden. Wenn man jedoch nur $N$ hat und die ursprünglichen $p$ und $q$ finden möchte, würde dies so lange dauern, dass selbst der derzeit schnellste Supercomputer das Problem in Billionen von Jahren nicht lösen könnte.&lt;/p>
&lt;p>Diese &lt;strong>„Asymmetrie der Berechnung (in eine Richtung einfach, in die andere extrem schwer)“&lt;/strong> bildet die Grundlage für die Beziehung zwischen dem öffentlichen und dem privaten Schlüssel.&lt;/p>
&lt;hr>
&lt;h2 id="3-mathematische-grundlagen-der-rsa-verschlüsselung-teil-2-kongruenzen-modulo-arithmetik">3. Mathematische Grundlagen der RSA-Verschlüsselung Teil 2: Kongruenzen (Modulo-Arithmetik)
&lt;/h2>&lt;p>Die Berechnungen bei der RSA-Verschlüsselung erfolgen nicht wie bei der gewöhnlichen Addition oder Multiplikation, bei der die Zahlen unendlich groß werden, sondern in einer Welt der „Reste“, die entstehen, wenn man durch eine bestimmte Zahl teilt. Dies wird als &lt;strong>Kongruenz (Modulo-Arithmetik)&lt;/strong> bezeichnet.&lt;/p>
&lt;h3 id="31-die-mathematik-der-uhr">3.1 Die Mathematik der Uhr
&lt;/h3>&lt;p>Die Modulo-Arithmetik wird oft mit der „Mathematik der Uhr“ verglichen. Angenommen, es ist jetzt 10 Uhr, wie spät ist es in 5 Stunden? $10 + 5 = 15$ Uhr, aber auf einer normalen 12-Stunden-Uhr würden wir „3 Uhr“ sagen. Das liegt daran, dass der Rest von 15 geteilt durch 12 gleich 3 ist.&lt;/p>
&lt;p>In der Welt der Mathematik wird dies wie folgt geschrieben:
&lt;/p>
$$ 15 \equiv 3 \pmod{12} $$
&lt;p>
Man liest dies als: „15 und 3 sind kongruent modulo 12 (sie haben denselben Rest, wenn man sie durch 12 teilt).“&lt;/p>
&lt;h3 id="32-grundlegende-eigenschaften-von-kongruenzen">3.2 Grundlegende Eigenschaften von Kongruenzen
&lt;/h3>&lt;p>Kongruenzen haben sehr nützliche Eigenschaften, die denen einer Gleichung ($=$) sehr ähnlich sind. Sei das Modul (die Zahl, durch die geteilt wird) $N$.
Wenn $a \equiv b \pmod N$ und $c \equiv d \pmod N$, dann gilt Folgendes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Addition:&lt;/strong> $a + c \equiv b + d \pmod N$&lt;/li>
&lt;li>&lt;strong>Subtraktion:&lt;/strong> $a - c \equiv b - d \pmod N$&lt;/li>
&lt;li>&lt;strong>Multiplikation:&lt;/strong> $a \times c \equiv b \times d \pmod N$&lt;/li>
&lt;li>&lt;strong>Potenzieren:&lt;/strong> $a^k \equiv b^k \pmod N$ (wobei $k$ eine natürliche Zahl ist)&lt;/li>
&lt;/ol>
&lt;p>Besonders wichtig ist die Eigenschaft des „Potenzierens“. Das bedeutet: &lt;strong>„Die Potenz eines Restes ist kongruent zum Rest der Potenz.“&lt;/strong>
Angenommen, wir wollen den Rest ermitteln, wenn $7^{100}$ durch $5$ geteilt wird. Es wäre mühsam, $7$ ernsthaft 100 Mal mit sich selbst zu multiplizieren und dann durch $5$ zu teilen. Wenn wir jedoch die Eigenschaften von Kongruenzen nutzen, wissen wir, dass $7 \equiv 2 \pmod 5$ ist, also gilt $7^{100} \equiv 2^{100} \pmod 5$. Dadurch lässt sich die Berechnung drastisch vereinfachen. Da in der Welt der Kryptographie mit sehr großen Potenzen gerechnet wird, ist diese Eigenschaft unverzichtbar.&lt;/p>
&lt;hr>
&lt;h2 id="4-mathematische-grundlagen-der-rsa-verschlüsselung-teil-3-eulersche-phi-funktion-und-der-satz-von-euler">4. Mathematische Grundlagen der RSA-Verschlüsselung Teil 3: Eulersche Phi-Funktion und der Satz von Euler
&lt;/h2>&lt;p>Hier beginnt nun die magische Mathematik, die den Kern der RSA-Verschlüsselung ausmacht. Hier kommt der „Satz von Euler“ ins Spiel, der eine Verallgemeinerung des „kleinen fermatschen Satzes“ ist.&lt;/p>
&lt;h3 id="41-die-eulersche-totient-funktion-phin">4.1 Die Eulersche Totient-Funktion $\phi(N)$
&lt;/h3>&lt;p>Die Eulersche Totient-Funktion (auch $\phi$-Funktion genannt) gibt für eine natürliche Zahl $N$ &lt;strong>„die Anzahl der natürlichen Zahlen von 1 bis $N$, die teilerfremd zu $N$ sind (deren größter gemeinsamer Teiler 1 ist)“&lt;/strong>, zurück.&lt;/p>
&lt;p>Schauen wir uns einige Beispiele an.&lt;/p>
&lt;ul>
&lt;li>$\phi(5)$: Unter 1, 2, 3, 4 und 5 sind 1, 2, 3 und 4 teilerfremd zu 5 (also 4 Zahlen). Daher ist $\phi(5) = 4$.&lt;/li>
&lt;li>$\phi(6)$: Unter 1, 2, 3, 4, 5 und 6 sind 1 und 5 teilerfremd zu 6 (also 2 Zahlen). Daher ist $\phi(6) = 2$.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Spezielle Eigenschaft bei Primzahlen]&lt;/strong>
Wenn $p$ eine Primzahl ist, sind alle Zahlen von 1 bis $p-1$ teilerfremd zu $p$. Daher gilt:
&lt;/p>
$$ \phi(p) = p - 1 $$
&lt;p>&lt;strong>[Spezielle Eigenschaft beim Produkt von Primzahlen]&lt;/strong>
Für zwei verschiedene Primzahlen $p$ und $q$ und $N = p \times q$ kann $\phi(N)$ einfach durch folgende Berechnung ermittelt werden:
&lt;/p>
$$ \phi(N) = \phi(p) \times \phi(q) = (p - 1)(q - 1) $$
&lt;p>
Diese Eigenschaft fungiert als „geheime Hintertür (Trapdoor)“ der RSA-Verschlüsselung. Wer $p$ und $q$ kennt (der Ersteller des Schlüssels), kann $\phi(N)$ augenblicklich berechnen, aber ein Dritter, der nur $N$ kennt, kann $\phi(N)$ nicht bestimmen, ohne $N$ vorher in seine Primfaktoren zu zerlegen.&lt;/p>
&lt;h3 id="42-der-satz-von-euler">4.2 Der Satz von Euler
&lt;/h3>&lt;p>Leonhard Euler bewies mit Hilfe dieser Funktion $\phi(N)$ den folgenden schönen Satz.&lt;/p>
&lt;p>&lt;strong>Der Satz von Euler:&lt;/strong>
Wenn eine ganze Zahl $a$ und $N$ teilerfremd sind, gilt die folgende Kongruenz:
&lt;/p>
$$ a^{\phi(N)} \equiv 1 \pmod N $$
&lt;p>Das ist die bemerkenswerte Eigenschaft, dass „wenn man eine bestimmte Zahl $a$ insgesamt $\phi(N)$ Mal mit sich selbst multipliziert und durch $N$ teilt, der Rest immer $1$ ist“. (Wenn $N$ eine Primzahl $p$ ist, ergibt sich $a^{p-1} \equiv 1 \pmod p$, was als der kleine fermatsche Satz bekannt ist).&lt;/p>
&lt;p>Lassen Sie uns diesen Satz von Euler umformen. Wir multiplizieren beide Seiten noch einmal mit $a$.
&lt;/p>
$$ a^{\phi(N) + 1} \equiv a \pmod N $$
&lt;p>Darüber hinaus ergibt $a^{k \cdot \phi(N)}$ für eine beliebige ganze Zahl $k$ ebenfalls $1^k = 1$, sodass die folgende Gleichung gilt:
&lt;/p>
$$ a^{k \cdot \phi(N) + 1} \equiv a \pmod N $$
&lt;p>Genau diese Gleichung ist das Grundprinzip, das die Magie der RSA-Verschlüsselung – &lt;strong>„nach dem Verschlüsseln und Entschlüsseln erhält man wieder das Original“&lt;/strong> – möglich macht.&lt;/p>
&lt;hr>
&lt;h2 id="5-der-algorithmus-der-rsa-verschlüsselung-schritte-zur-schlüsselerzeugung-verschlüsselung-und-entschlüsselung">5. Der Algorithmus der RSA-Verschlüsselung: Schritte zur Schlüsselerzeugung, Verschlüsselung und Entschlüsselung
&lt;/h2>&lt;p>Da wir nun über das Grundlagenwissen verfügen, schauen wir uns endlich die konkreten Schritte der RSA-Verschlüsselung an. Die RSA-Verschlüsselung ist grob in drei Phasen unterteilt: „1. Schlüsselerzeugung“, „2. Verschlüsselung“ und „3. Entschlüsselung“.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Wähle Primzahlen p, q"] --> A2["Berechne N = p × q"]
A1 --> A3["Berechne φ(N) = (p-1)(q-1)"]
A3 --> A4["Wähle ein e, das zu φ(N) teilerfremd ist"]
A3 --> A5["Berechne d, sodass e × d ≡ 1 (mod φ(N))"]
A2 --> A6["Öffentlicher Schlüssel (N, e)"]
A4 --> A6
A5 --> A7["Privater Schlüssel d"]
B1["2. Klartext-Nachricht M"] --> B2["Berechne C ≡ M^e (mod N)"]
A6 -.-> B2
B2 --> B3["Sende Geheimtext C"]
B3 --> C1["3. Empfangener Geheimtext C"]
C1 --> C2["Berechne M ≡ C^d (mod N)"]
A7 -.-> C2
C2 --> C3["Erhalte ursprüngliche Klartext-Nachricht M"]&lt;/div>
&lt;h3 id="51-schlüsselerzeugung-key-generation">5.1 Schlüsselerzeugung (Key Generation)
&lt;/h3>&lt;p>Bob, der Empfänger, generiert für sich selbst einen „öffentlichen Schlüssel“ und einen „privaten Schlüssel“.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Wahl der Primzahlen:&lt;/strong> Es werden zwei große Primzahlen $p$ und $q$ zufällig gewählt.&lt;/li>
&lt;li>&lt;strong>Berechnung des Moduls $N$:&lt;/strong> Es wird $N = p \times q$ berechnet. Dieses $N$ wird veröffentlicht.&lt;/li>
&lt;li>&lt;strong>Berechnung von $\phi(N)$:&lt;/strong> Die Eulersche Funktion $\phi(N) = (p - 1)(q - 1)$ wird berechnet. Diese Zahl ist Bobs Geheimnis.&lt;/li>
&lt;li>&lt;strong>Wahl des öffentlichen Schlüssels $e$:&lt;/strong> Es wird eine ganze Zahl $e$ gewählt, für die $1 &lt; e &lt; \phi(N)$ gilt und die teilerfremd zu $\phi(N)$ ist.&lt;/li>
&lt;li>&lt;strong>Berechnung des privaten Schlüssels $d$:&lt;/strong> Es wird eine ganze Zahl $d$ gefunden, die folgende Bedingung erfüllt:
$$ e \times d \equiv 1 \pmod{\phi(N)} $$
Das bedeutet: „Eine Zahl $d$, bei der $e \times d$ geteilt durch $\phi(N)$ einen Rest von $1$ ergibt.“&lt;/li>
&lt;/ol>
&lt;p>Damit sind die Schlüssel vorbereitet.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Öffentlicher Schlüssel:&lt;/strong> Das Paar $(N, e)$. Es wird für die ganze Welt veröffentlicht.&lt;/li>
&lt;li>&lt;strong>Privater Schlüssel:&lt;/strong> $d$. Er wird absolut niemandem verraten.&lt;/li>
&lt;/ul>
&lt;h3 id="52-verschlüsselung-encryption">5.2 Verschlüsselung (Encryption)
&lt;/h3>&lt;p>Angenommen, Alice möchte Bob eine geheime Nachricht $M$ senden. ($M$ ist ein in Zahlen umgewandelter Text, wobei $0 \le M &lt; N$ gilt). Alice berechnet mithilfe von Bobs öffentlichem Schlüssel $(N, e)$ Folgendes:&lt;/p>
$$ C \equiv M^e \pmod N $$
&lt;p>Sie berechnet „den Rest $C$, der entsteht, wenn man die Nachricht $M$ mit $e$ potenziert und durch $N$ teilt“. Dieses $C$ ist der Geheimtext.&lt;/p>
&lt;h3 id="53-entschlüsselung-decryption">5.3 Entschlüsselung (Decryption)
&lt;/h3>&lt;p>Bob empfängt den Geheimtext $C$. Bob rechnet mit seinem privaten Schlüssel $d$ wie folgt:&lt;/p>
$$ M \equiv C^d \pmod N $$
&lt;p>Wenn man „den Rest berechnet, der entsteht, wenn der Geheimtext $C$ mit $d$ potenziert und durch $N$ geteilt wird“, wird erstaunlicherweise die ursprüngliche Nachricht $M$ wiederhergestellt!&lt;/p>
&lt;hr>
&lt;h2 id="6-warum-erhält-man-durch-entschlüsselung-das-original-zurück-mathematischer-beweis">6. Warum erhält man durch Entschlüsselung das Original zurück? (Mathematischer Beweis)
&lt;/h2>&lt;p>Vielleicht fragen Sie sich: „Wie kann es sein, dass $C$ hoch $d$ uns einfach unser ursprüngliches $M$ zurückgibt?“ Hier kommt nun der zuvor erwähnte „Satz von Euler“ ins Spiel.&lt;/p>
&lt;p>Setzen wir die Formel für die Verschlüsselung, $C = M^e$, in die Berechnungsformel für die Entschlüsselung, $C^d \pmod N$, ein.
&lt;/p>
$$ C^d \equiv (M^e)^d \equiv M^{ed} \pmod N $$
&lt;p>Erinnern wir uns nun an Schritt 5 der Schlüsselerzeugung. Als Bob $d$ generierte, wählte er es so, dass $e \times d \equiv 1 \pmod{\phi(N)}$ gilt. Das bedeutet, dass „$ed$ ein Vielfaches von $\phi(N)$ plus $1$ ist“. Mit einer ganzen Zahl $k$ lässt sich dies wie folgt schreiben:
&lt;/p>
$$ ed = k \cdot \phi(N) + 1 $$
&lt;p>Wir setzen dies in den Exponenten ein und zerlegen ihn mithilfe der Potenzgesetze.
&lt;/p>
$$ M^{ed} = M^{k \cdot \phi(N) + 1} = M^{k \cdot \phi(N)} \times M^1 = (M^{\phi(N)})^k \times M $$
&lt;p>Wenn wir hier annehmen, dass die Nachricht $M$ und $N$ teilerfremd sind, gilt nach dem &lt;strong>Satz von Euler&lt;/strong> $M^{\phi(N)} \equiv 1 \pmod N$.
&lt;/p>
$$ (M^{\phi(N)})^k \times M \equiv 1^k \times M \equiv M \pmod N $$
&lt;p>Folglich ergibt sich auf wunderbare Weise diese Gleichung:
&lt;/p>
$$ C^d \equiv M \pmod N $$
&lt;p>Da Alice $d$ nicht kennt und auch ein Abhörer $d$ nicht kennt, ist Bob mit seinem $d$ der Einzige, der $M$ aus $C$ extrahieren kann.&lt;/p>
&lt;hr>
&lt;h2 id="7-ein-konkretes-beispiel-rsa-mit-kleinen-primzahlen-per-hand-ausprobieren">7. Ein konkretes Beispiel: RSA mit kleinen Primzahlen per Hand ausprobieren
&lt;/h2>&lt;p>Lassen Sie uns tatsächlich eine verschlüsselte Kommunikation von Alice an Bob unter Verwendung kleiner Zahlen (Primzahlen) durchführen.&lt;/p>
&lt;p>&lt;strong>[Bobs Schlüsselerzeugungsphase]&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Er wählt zwei Primzahlen: $p=11$, $q=13$.&lt;/li>
&lt;li>Er berechnet $N = 11 \times 13 = 143$.&lt;/li>
&lt;li>Er berechnet $\phi(N) = (11 - 1) \times (13 - 1) = 10 \times 12 = 120$.&lt;/li>
&lt;li>Er wählt einen öffentlichen Schlüssel $e$, der teilerfremd zu $\phi(N)=120$ ist. Hier wählen wir $e=7$.&lt;/li>
&lt;li>Er ermittelt den privaten Schlüssel $d$. Er sucht ein $d$, für das $7 \times d \equiv 1 \pmod{120}$ gilt.
In der Gleichung $7d = 120k + 1$ ergibt sich für $k=6$ die Zahl $721$, und $721 \div 7 = 103$.
Daher ist $d = 103$.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Öffentlicher Schlüssel: $(N=143, e=7)$&lt;/li>
&lt;li>Privater Schlüssel: $d=103$&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>[Alices Verschlüsselungsphase]&lt;/strong>
Nehmen wir an, sie möchte die Nachricht $M = 9$ senden.
Formel: $C \equiv 9^7 \pmod{143}$
$9^7 = 4.782.969$. Teilt man dies durch 143, erhält man $33447$ mit dem Rest $48$.
Der Geheimtext $C$ ist also $48$.&lt;/p>
&lt;p>&lt;strong>[Bobs Entschlüsselungsphase]&lt;/strong>
Bob empfängt den Geheimtext $C = 48$ und entschlüsselt ihn mit dem privaten Schlüssel $d = 103$.
Formel: $M \equiv 48^{103} \pmod{143}$
Wenn man &lt;code>(48 ** 103) % 143&lt;/code> in einen Computer eingibt, ist das großartige Ergebnis tatsächlich „&lt;strong>9&lt;/strong>“! Er hat die ursprüngliche Nachricht sicher erhalten.&lt;/p>
&lt;hr>
&lt;h2 id="8-wie-man-den-privaten-schlüssel-d-findet-der-erweiterte-euklidische-algorithmus">8. Wie man den privaten Schlüssel $d$ findet: Der erweiterte euklidische Algorithmus
&lt;/h2>&lt;p>Im per Hand berechneten Beispiel haben wir $k$ durch Ausprobieren gesucht, um $d=103$ zu finden, aber wenn die Zahlen Hunderte von Ziffern haben, ist diese Methode unmöglich. In echten Programmen wird ein Algorithmus namens &lt;strong>„erweiterter euklidischer Algorithmus“&lt;/strong> verwendet.&lt;/p>
&lt;p>Die Lösung von $7d \equiv 1 \pmod{120}$ ist gleichbedeutend mit dem Finden der ganzen Zahlen $d$ und $y$, die $7d + 120y = 1$ erfüllen. Durch die Rückwärtsrechnung des euklidischen Algorithmus lässt sich dies maschinell ermitteln.&lt;/p>
&lt;ol>
&lt;li>$120 \div 7 = 17$ Rest $1$&lt;/li>
&lt;li>Durch Umformen ergibt sich: $1 = 120 - 17 \times 7$&lt;/li>
&lt;li>Das heißt: $-17 \times 7 \equiv 1 \pmod{120}$&lt;/li>
&lt;/ol>
&lt;p>$-17$ bedeutet in der Welt von Modulo $120$ dasselbe wie $120 - 17 = 103$. So lässt sich $d = 103$ augenblicklich ermitteln. Diese Methode erlaubt sehr schnelle Berechnungen, egal wie riesig die Zahlen sind.&lt;/p>
&lt;hr>
&lt;h2 id="9-ein-weiteres-gesicht-der-rsa-verschlüsselung-digitale-signaturen">9. Ein weiteres Gesicht der RSA-Verschlüsselung: Digitale Signaturen
&lt;/h2>&lt;p>Das Tolle an der RSA-Verschlüsselung ist, dass sie auch als &lt;strong>„digitale Signatur“&lt;/strong> verwendet werden kann, indem man die Rollen des öffentlichen und privaten Schlüssels vertauscht.&lt;/p>
&lt;p>Während bei der Verschlüsselung das Prinzip „Verschlüsseln mit dem öffentlichen Schlüssel $\Rightarrow$ Entschlüsseln mit dem privaten Schlüssel“ galt,
ist der Ablauf bei der digitalen Signatur „Verschlüsseln mit dem privaten Schlüssel $\Rightarrow$ Entschlüsseln mit dem öffentlichen Schlüssel“.&lt;/p>
&lt;div class="mermaid">flowchart TD
A1["1. Alice erstellt Signatur mit privatem Schlüssel"] --> A2["S ≡ M^d (mod N)"]
A2 --> A3["Sende Nachricht M und Signatur S"]
A3 --> B1["2. Bob verifiziert Signatur mit öffentlichem Schlüssel"]
B1 --> B2["Berechne M' ≡ S^e (mod N)"]
B2 --> B3["Prüfe, ob M' und M übereinstimmen"]&lt;/div>
&lt;p>Alice wandelt die Nachricht mit ihrem eigenen privaten Schlüssel $d$ um (das ist die Signatur $S$) und sendet sie an Bob. Bob führt mit Alices öffentlichem Schlüssel $e$ die Verifizierungsberechnung durch. Wenn das Ergebnis der Berechnung mit der ursprünglichen Nachricht übereinstimmt, beweist dies gleichzeitig, dass „die Daten nur mit Alices privatem Schlüssel erstellt werden konnten“ und dass „die Nachricht unterwegs nicht manipuliert wurde“.&lt;/p>
&lt;hr>
&lt;h2 id="10-die-rsa-verschlüsselung-durch-programmierung-erfahren">10. Die RSA-Verschlüsselung durch Programmierung erfahren
&lt;/h2>&lt;p>Potenzberechnungen, die per Hand extrem mühsam sind, lassen sich in Python sehr einfach implementieren. Im Folgenden finden Sie einen Python-Code, mit dem Sie die Kernlogik der RSA-Verschlüsselung erleben können.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">gcd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Den größten gemeinsamen Teiler finden&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">b&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Den privaten Schlüssel d finden (nutzt die eingebaute Funktion ab Python 3.8)&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Schlüsselerzeugung&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">11&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">p&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">q&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">phi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">q&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_inverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">phi&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Öffentlicher Schlüssel: (N=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">, e=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">), Privater Schlüssel: d=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Verschlüsselung&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ciphertext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Geheimtext: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Entschlüsselung&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">decrypted_message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ciphertext&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Entschlüsselte Nachricht: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">decrypted_message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Die Python-Funktion &lt;code>pow(base, exp, mod)&lt;/code> verwendet intern einen schnellen Algorithmus, der sich „binäre Exponentiation (Square-and-Multiply)“ nennt. Dadurch wird die Berechnung auch bei Zahlen mit Hunderten von Ziffern in einem Augenblick abgeschlossen.&lt;/p>
&lt;hr>
&lt;h2 id="11-zusammenfassung-und-zukünftige-kryptographietechnologien">11. Zusammenfassung und zukünftige Kryptographietechnologien
&lt;/h2>&lt;p>Basierend auf dem Wissen der Schulmathematik haben wir die Funktionsweise der RSA-Verschlüsselung aufgedeckt.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Die Schwierigkeit der Primfaktorzerlegung:&lt;/strong> $p \times q = N$ ist einfach, aber es ist sehr schwer, $p$ und $q$ aus $N$ zu finden.&lt;/li>
&lt;li>&lt;strong>Kongruenzen und der Satz von Euler:&lt;/strong> Das Gesetz $a^{\phi(N)} \equiv 1 \pmod N$ vervollständigt die magische Trapdoor, bei der man „durch Potenzieren mit einer bestimmten Zahl zum Ursprung zurückkehrt“.&lt;/li>
&lt;li>&lt;strong>Öffentlicher und privater Schlüssel:&lt;/strong> Jeder kann verschlüsseln, aber nur der rechtmäßige Empfänger kann entschlüsseln.&lt;/li>
&lt;/ol>
&lt;p>Das $N$ der heute verwendeten RSA-Verschlüsselung hat mehr als 600 Ziffern. Selbst wenn alle Supercomputer der Welt zusammenarbeiten würden, würde die Primfaktorzerlegung länger dauern als das Alter des Universums. Wenn jedoch in Zukunft die derzeit in Entwicklung befindlichen „Quantencomputer“ in die Praxis umgesetzt werden, besteht die Möglichkeit, dass diese Primfaktorzerlegung durch den „Shor-Algorithmus“ augenblicklich geknackt wird. Aus diesem Grund wird derzeit weltweit mit Hochdruck an der Entwicklung der „Post-Quanten-Kryptographie“ gearbeitet, die selbst von Quantencomputern nicht entschlüsselt werden kann.&lt;/p>
&lt;p>Fortgeschrittene Mathematik, die oft als „nutzlos“ abgetan wird, schützt in Wirklichkeit unseren Alltag grundlegend. Die RSA-Verschlüsselung ist das beste Lehrmaterial, um uns diese Tiefe und Schönheit der Mathematik zu zeigen. Ich hoffe, dass Sie durch diesen Artikel ein wenig von der Faszination für Kryptographie und Mathematik spüren konnten.&lt;/p></description></item></channel></rss>