Das Problem mit den Primzahlen
Primzahlen sind das Fundament moderner Verschlüsselung. RSA-2048 verwendet Primzahlen mit über 600 Dezimalstellen, die täglich Billionen von Transaktionen schützen. Das eigentliche Problem liegt nicht im Finden dieser Primzahlen, sondern in ihrer Sicherheit gegen zukünftige Angriffe.
Tabelle 1 - Kryptographische Primzahlgrößen im Einsatz
Standard | Bit-Größe | Dezimalstellen | Erzeugungszeit | Sicherheit bis |
RSA-1024 | 1024 | ~308 | 10ms | Gebrochen (2010) |
RSA-2048 | 2048 | ~617 | 50ms | ~2030 |
RSA-3072 | 3072 | ~925 | 200ms | ~2050 |
RSA-4096 | 4096 | ~1233 | 800ms | ~2080 |
Post-Quantum | - | - | - | Quanten-resistent |
/* Miller-Rabin Primzahltest - Industriestandard */
#include <random>
#include <gmp.h>
bool miller_rabin_test(const mpz_t n, int iterations = 40) {
if (mpz_cmp_ui(n, 2) == 0) return true;
if (mpz_even_p(n) || mpz_cmp_ui(n, 2) < 0) return false;
mpz_t d, n_minus_1, a, x;
mpz_inits(d, n_minus_1, a, x, NULL);
mpz_sub_ui(n_minus_1, n, 1);
mpz_set(d, n_minus_1);
// d = n-1, r = 0
int r = 0;
while (mpz_even_p(d)) {
mpz_fdiv_q_2exp(d, d, 1);
r++;
}
std::random_device rd;
gmp_randstate_t state;
gmp_randinit_default(state);
gmp_randseed_ui(state, rd());
for (int i = 0; i < iterations; i++) {
mpz_urandomm(a, state, n_minus_1);
mpz_add_ui(a, a, 2); // a in [2, n-1]
mpz_powm(x, a, d, n);
if (mpz_cmp_ui(x, 1) == 0 || mpz_cmp(x, n_minus_1) == 0)
continue;
bool composite = true;
for (int j = 0; j < r - 1; j++) {
mpz_powm_ui(x, x, 2, n);
if (mpz_cmp(x, n_minus_1) == 0) {
composite = false;
break;
}
}
if (composite) {
mpz_clears(d, n_minus_1, a, x, NULL);
gmp_randclear(state);
return false;
}
}
mpz_clears(d, n_minus_1, a, x, NULL);
gmp_randclear(state);
return true;
}
// Generiert eine kryptographisch sichere Primzahl
void generate_crypto_prime(mpz_t result, int bits) {
gmp_randstate_t state;
gmp_randinit_default(state);
do {
mpz_urandomb(result, state, bits);
mpz_setbit(result, bits - 1); // Höchstes Bit setzen
mpz_setbit(result, 0); // Ungerade machen
} while (!miller_rabin_test(result));
gmp_randclear(state);
}
#include <iostream>
int main() {
// Teste bekannte Primzahlen
mpz_t test_num;
mpz_init(test_num);
mpz_set_ui(test_num, 1009);
std::cout << "1009 ist " << (miller_rabin_test(test_num) ? "prim" : "zusammengesetzt") << std::endl;
mpz_set_ui(test_num, 1010);
std::cout << "1010 ist " << (miller_rabin_test(test_num) ? "prim" : "zusammengesetzt") << std::endl;
// Teste große Mersenne-Primzahl 2^31-1
mpz_set_ui(test_num, 2147483647UL);
std::cout << "2^31-1 ist " << (miller_rabin_test(test_num) ? "prim" : "zusammengesetzt") << std::endl;
// Generiere 128-Bit und 256-Bit Primzahlen
mpz_t prime128, prime256;
mpz_init(prime128);
mpz_init(prime256);
std::cout << "Generiere 128-Bit Primzahl..." << std::endl;
generate_crypto_prime(prime128, 128);
std::cout << "128-Bit Primzahl: ";
mpz_out_str(stdout, 10, prime128);
std::cout << std::endl;
std::cout << "Generiere 256-Bit Primzahl..." << std::endl;
generate_crypto_prime(prime256, 256);
std::cout << "256-Bit Primzahl: ";
mpz_out_str(stdout, 10, prime256);
std::cout << std::endl;
mpz_clear(test_num);
mpz_clear(prime128);
mpz_clear(prime256);
return 0;
}
Installation: sudo apt install libgmp-dev
Kompilieren: g++ main.cpp -lgmp -o main
Die wahren Herausforderungen:
• Quantencomputer-Bedrohung: Shor's Algorithmus kann RSA in polynomieller Zeit brechen
• Post-Quantum-Kryptographie: Übergang zu Gitter-basierten, Hash-basierten und Code-basierten Verfahren
• Hardwarebeschleunigung: Spezialisierte ASICs für Faktorisierung werden immer leistungsfähiger
• Seitenkanalangriffe: Timing-, Power- und EM-Attacken auf Primzahloperationen
Aktuelle Entwicklungen:
NIST hat 2024 neue Post-Quantum-Standards veröffentlicht. Große Tech-Konzerne migrieren bereits ihre Systeme. Die Ära der RSA-Primzahlen neigt sich dem Ende zu - nicht wegen Berechnungsproblemen, sondern wegen fundamentaler physikalischer Durchbrüche.
Fazit:
Primzahlen waren 50 Jahre lang die Basis der Internetsicherheit. Ihre Berechnung ist trivial, ihre Faktorisierung war schwer. Quantencomputer machen beides trivial. Die Zukunft gehört anderen mathematischen Strukturen.
Die Primzahlfunktion π(n)
Betrachten wir die Primzahlfunktion π(n) etwas genauer (siehe Tabelle 2 weiter unten).
Die Primzahlfunktion \(\pi(n)\) gibt die Anzahl der Primzahlen bis zur Zahl n an.
$$
\pi(n) = \{ p \in \mathbb{P} | p \leq n \}
$$
Seit Gauss gibt es für \( \pi(x) \) eine erste Schätzung aus empirischen Daten bis \(10^6\).
$$
{\pi(n)} \sim {n \over \ln(n)}
$$
Tabelle 2 - Primzahlfunktion π(n) und Gauss'sche Näherung
n | π(n) | n/ln(n) | Fehler | Rel. Fehler |
100 | 25 | 22 | 3 | 12% |
1.000 | 168 | 144 | 24 | 14% |
10.000 | 1.229 | 1.086 | 143 | 12% |
100.000 | 9.592 | 8.686 | 906 | 9% |
1.000.000 | 78.498 | 72.382 | 6.116 | 8% |
10.000.000 | 664.579 | 620.421 | 44.158 | 7% |
100.000.000 | 5.761.455 | 5.428.681 | 332.774 | 6% |
Implementierung der Primzahlfunktion
Das folgende Beispiel berechnet \(\pi(n)\) bis zur Million und vergleicht die Ergebnisse systematisch mit der Gauss'schen Näherung. Die Implementierung verwendet das effiziente Sieb des Eratosthenes:
#!/usr/bin/env python3
import numpy as np
import math
import matplotlib.pyplot as plt
def sieve_of_eratosthenes(limit):
"""Effizientes Sieb des Eratosthenes mit NumPy"""
is_prime = np.ones(limit + 1, dtype=bool)
is_prime[0] = is_prime[1] = False
for i in range(2, int(math.sqrt(limit)) + 1):
if is_prime[i]:
is_prime[i*i:limit+1:i] = False
return is_prime
def calculate_pi_function(limit):
"""Berechnet π(n) für alle n bis limit"""
primes = sieve_of_eratosthenes(limit)
pi_values = np.cumsum(primes)
return pi_values
def gauss_approximation(n):
"""Gauss'sche Näherung: n/ln(n)"""
return n / np.log(n) if n > 1 else 0
# Berechnung und Vergleich
limit = 1000000
pi_values = calculate_pi_function(limit)
# Erstelle Datenpunkte für Analyse
sample_points = np.logspace(2, 6, 50, dtype=int) # 100 bis 1,000,000
errors = []
print("n\t\tπ(n)\t\tn/ln(n)\t\tFehler")
print("-" * 50)
for n in sample_points:
if n <= limit:
pi_n = pi_values[n]
gauss_n = gauss_approximation(n)
error = pi_n - gauss_n
errors.append(error)
print(f"{n:8d}\t{pi_n:8d}\t{gauss_n:8.0f}\t{error:8.0f}")
# Visualisierung der Fehlerverteilung
plt.figure(figsize=(10, 6))
plt.loglog(sample_points[:len(errors)], np.abs(errors))
plt.xlabel('n')
plt.ylabel('|π(n) - n/ln(n)|')
plt.title('Fehler der Gauss-Näherung')
plt.grid(True)
plt.show()
Mit diesen Mitteln kann mann nun eine Fehlerfunktion definieren und zumindest einige der Fehler ausrechnen.
$$ err(n) = \pi(n) - {n \over \ln(n)} $$
Tabelle 3 - Fehleranalyse der Gauss'schen Näherung
n | π(n) | n/ln(n) | err(n) |
... | | | |
1.000 | 168 | 144 | 24 |
2.000 | 303 | 263 | 40 |
... | | | |
11.000 | 1.335 | 1182 | 153 |
12.000 | 1.438 | 1277 | 161 |
... | | | |
101.000 | 9.673 | 8765 | 908 |
102.000 | 9.766 | 8844 | 922 |
... | | | |
1.001.000 | 78.573 | 72449 | 6124 |
1.002.000 | 78.650 | 72516 | 6134 |
... | | | |
5.001.000 | 348.576 | 324210 | 24366 |
5.002.000 | 348.641 | 324271 | 24370 |
... | | | |
15.485.917 | 1.000.000 | 935.397 | 64.604 |
32.452.883 | 2.000.000 | 1.876.398 | 123.602 |
49.979.737 | 3.000.000 | 2.819.392 | 180.608 |
67.867.999 | 4.000.000 | 3.763.528 | 236.472 |
86.028.221 | 5.000.000 | 4.708.666 | 291.334 |
104.395.337 | 6.000.000 | 5.654.086 | 345.914 |
122.949.839 | 7.000.000 | 6.600.523 | 399.477 |
141.650.981 | 8.000.000 | 7.547.120 | 452.880 |
160.481.221 | 9.000.000 | 8.493.906 | 506.094 |
179.424.697 | 10.000.000 | 9.440.788 | 559.212 |
198.491.369 | 11.000.000 | 10.388.815 | 611.185 |
217.645.201 | 12.000.000 | 11.336.645 | 663.355 |
Aus der Fehlerfunktion \(err(n) = \pi(n) - {n \over \ln(n)}\) lassen sich weitere wichtige Eigenschaften ablesen:
- Der Fehler wächst systematisch mit \(n\), was zeigt, dass die Gauss'sche Näherung strukturell zu klein ist
- Das Verhältnis \({err(n) \over \pi(n)}\) wird jedoch mit steigendem \(n\) kleiner - die relative Genauigkeit verbessert sich
- Legendre versuchte 1808 eine Verbesserung mit \({\pi(n) \sim {n \over {\ln(n) - 1.08366}}}\), was für mittlere Bereiche besser funktionierte, aber für sehr große n wieder schlechter wird
- Gauss erkannte 1849, dass die Integrallogarithmus-Funktion \(Li(n) = \int_2^n {dt \over \ln(t)}\) eine deutlich bessere Approximation liefert als alle bisherigen Ansätze
Diese Verbesserungen führten schließlich zum
Primzahlsatz, der besagt: \(\lim_{n \to \infty} {\pi(n) \over {n / \ln(n)}} = 1\).
Der Primzahlsatz wurde 1896 unabhängig von Hadamard und de la Vallée-Poussin bewiesen und stellt einen Höhepunkt der analytischen Zahlentheorie dar. Er bestätigt, dass Gauss' ursprüngliche Intuition asymptotisch exakt war.
Der Primzahlsatz zeigt, dass Primzahlen zwar immer seltener werden, aber nach einem klaren Gesetz: Die Wahrscheinlichkeit, dass eine zufällige Zahl um n prim ist, beträgt etwa \(1 / \ln(n)\). Bei einer Milliarde ist etwa jede 20. Zahl prim, bei einer Billion nur noch jede 28. Zahl.
Anzahl der Primzahlen
Wieviele Primzahlen gibt es? Diese Frage wurde bereits in verschiedenen Varianten beantwortet. Der Erste, der eine Antwort
herleiten konnte war Euklid. Angenommen es gäbe nur endlich viele Primzahlen \({p_1, p_2, p_3, ..., p_n}\). Dann kann man eine neue Zahl Z = \(p_1\cdot p_2 \cdot... \cdot p_n + 1\) bilden die durch keine der \(p_i\) teilbar ist. Es ist verlockend zu denken, Z sei automatisch Prim, doch dem ist nicht so. Vielmehr braucht man einen Hauptsatz der Zahlentheorie der ebenfalls seit Euklid bekannt ist, indem es heisst, dass jede Zahl aus Primfaktoren zusammengesetzt ist - so auch Z. Aber die Faktoren von Z sind nicht in unserer Liste \(p_i\) enthalten. Das ist aber ein Widerspruch zur Annahme, das \(p_i\) alle existierenden Primzahlen wären.
Harmonische Reihe
Ein Ausgangspunkt vieler Überlegungen ist die sogenannte harmonische Reihe
$$
\sum_{n=1}^{\infty} \frac{1}{n} = \frac{1}{1} + \frac{1}{2} + \frac{1}{3} + \frac{1}{4} + \cdots
$$
Die harmonische Reihe divergiert, obwohl ihre Glieder gegen Null gehen. Dies lässt sich einfach durch Umsortierung zeigen:
$$
1 + \frac{1}{2} + \left(\frac{1}{3} + \frac{1}{4}\right) + \left(\frac{1}{5} + \frac{1}{6} + \frac{1}{7} + \frac{1}{8}\right) + \cdots > 1 + \frac{1}{2} + \frac{1}{2} + \frac{1}{2} + \cdots
$$
Während die harmonische Reihe divergiert, konvergiert bereits \(\sum_{n=1}^{\infty} \frac{1}{n^2}\). Das
Basler Problem - die Bestimmung dieser Summe - beschäftigte Mathematiker über 90 Jahre, bis
Euler 1735 (im Alter von 28 Jahren) das verblüffende Ergebnis \(\frac{\pi^2}{6}\) fand.
Die interessante Frage ist: Wieviele Glieder kann man aus der harmonischen Reihe entfernen, sodass sie gerade noch divergiert? Ersetzt man die Glieder \(\frac{1}{n}\) durch \(\frac{1}{n^{1+\epsilon}}\) mit beliebig kleinem \(\epsilon > 0\), konvergiert die Reihe bereits. Die kritische Grenze liegt genau bei \(s = 1\) - jede noch so kleine Erhöhung des Exponenten führt zur Konvergenz.
Gamma-Funktion
Die Gamma-Funktion ist eine Verallgemeinerung der Fakultät auf reelle und komplexe Zahlen. Während \(n!\) nur für natürliche Zahlen definiert ist, ermöglicht \(\Gamma(s)\) Berechnungen wie \(1.5!\) oder \((2+3i)!\).
$$
\Gamma(s) = \int_0^\infty {t^{s-1} e^{-t}}dt, \quad \text{mit } Re(s) > 0
$$
$$
\Gamma(s+1) = \int_0^\infty {t^{(s+1)-1} e^{-t}}dt = \int_0^{\infty}t^s e^{-t} dt
$$
$$
= -e^{-t}t^s|_0^{\infty} - \int_0^{\infty}{{dt^s \over dt}e^{-t}}dt
$$
$$
= \int_0^{\infty} st^{s-1}e^{-t}dt = s\cdot\int_0^{\infty} t^{s-1}e^{-t}dt = s \cdot \Gamma(s)
$$
also
$$
\Gamma(s+1) = s \cdot \Gamma(s)
$$
Substituiere und setze in ursprüngliche Form ein:
$$
e^{-t} = \lim\limits_{n \to \infty}\left(1+\frac{-t}{n}\right)^n
$$
$$
\Gamma(s) = \int_0^{\infty}t^{s-1}e^{-t}dt
$$
$$
= \int_0^{\infty}t^{s-1}\lim\limits_{n \to \infty}\left(1+\frac{-t}{n}\right)^n dt
$$
$$
= \lim\limits_{n \to \infty} \int_0^n t^{s-1}\left(1-\frac{t}{n}\right)^n dt
$$
Nun kann man partiell integrieren:
$$
\int_0^n t^{s-1}\left( 1 - \frac{t}{n}\right)^n dt = \frac{1}{s} t^s\left(1-\frac{t}{n}\right)^n\bigg|_0^n - \int_0^n \frac{1}{s}t^s n\left(1- \frac{t}{n}\right)^{n-1}\left(-\frac{1}{n}\right)dt
$$
$$
= \frac{n}{s \cdot n} \int_0^n t^s\left(1- \frac{t}{n} \right)^{n-1}dt
$$
$$
= \frac{n}{s \cdot n} \cdot \frac{(n-1)}{n(s+1)} \int_0^n t^{s+1}\left(1-\frac{t}{n}\right)^{n-2}dt
$$
$$
= \frac{n}{s \cdot n} \cdot \frac{(n-1)}{n(s+1)} \cdot \frac{(n-2)}{n(s+2)} \int_0^n t^{s+2}\left(1-\frac{t}{n}\right)^{n-3}dt
$$
$$
= \frac{n}{s \cdot n} \cdot \frac{(n-1)}{n(s+1)} \cdot \frac{(n-2)}{n(s+2)} \cdots \frac{1}{n(s+n-1)} \int_0^n t^{s+n-1}dt
$$
$$
= \frac{n}{s \cdot n} \cdot \frac{(n-1)}{n(s+1)} \cdot \frac{(n-2)}{n(s+2)} \cdots \frac{1}{n(s+n-1)} \frac{t^{s+n}}{(s+n)} \bigg|_0^n
$$
$$
= \frac{n!}{n^n} n^{s+n} \prod_{i=0}^n (s+i)^{-1}
$$
$$
= \frac{n! n^s}{\prod_{i=0}^n(s+i)}
$$
Also
$$
\Gamma(s) = \lim\limits_{n \to \infty} ( {n^s \over s} \prod_{i=1}^n {i \over {(s+i)}})
$$
Wenn man in dieser Weise fortfährt, kann man zeigen, dass die Gamma-Funktion eng mit der Riemann-Zeta-Funktion verknüpft ist:
$$
\zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s} = \frac{1}{\Gamma(s)} \int_0^{\infty} \frac{t^{s-1}}{e^t - 1} dt
$$
Bereits 1737 entdeckte Euler die fundamentale Beziehung zwischen der Zeta-Funktion und den Primzahlen, die als Euler-Produktformel bekannt wurde:
$$
\prod_{\text{$p$ prim}} {1 \over {1 - {1 \over {p^s}}}} = {\sum_{n\ge1} {1 \over n^s}} = \zeta(s)
$$
Und das ist nun wirklich erstaunlich: Das unendliche Produkt aus lauter Primzahl-Inversen ergibt dasselbe wie die Summe aus lauter Inversen natürlicher Zahlen.
Die Riemann-Hypothese und moderne Primzahlforschung
Die Euler'sche Produktformel verbindet die Riemann-Zeta-Funktion direkt mit den Primzahlen und zeigt deren fundamentale Rolle in der Zahlentheorie. Die berühmte Riemann-Hypothese besagt, dass alle nichttrivialen Nullstellen von ζ(s) den Realteil 1/2 haben - ein Beweis würde präzise Aussagen über die Verteilung der Primzahlen ermöglichen.
Heute stehen wir an einem Wendepunkt: Während die klassische Primzahltheorie durch Quantencomputer bedroht wird, eröffnen sich neue Forschungsfelder in der Post-Quantum-Kryptographie. Die jahrtausendealten Primzahlen bleiben ein zentrales Element sowohl der reinen Mathematik als auch der angewandten Informatik.
Griechisches Alphabet
Α α | Alpha |
Β β | Beta |
Γ γ | Gamma |
Δ δ | Delta |
Ε ε | Epsilon |
Ζ ζ | Zeta |
Η η | Eta |
Θ θ | Theta |
Ι ι | Iota |
Κ κ | Kappa |
Λ λ | Lambda |
Μ μ | Mu |
Ν ν | Nu |
Ξ ξ | Xi |
Ο ο | Omikron |
Π π | Pi |
Ρ ρ | Rho |
Σ σ | Sigma |
Τ τ | Tau |
Υ υ | Upsilon |
Φ φ | Phi |
Χ χ | Chi |
Ψ ψ | Psi |
Ω ω | Omega |