Station - Faktorisierung

Primzahlen - Bausteine der natürliche Zahlen

Eine der wichtigsten Eigenschaften von Primzahlen ist, dass sie als Bausteine der natürlichen Zahlen angesehen werden können.

Satz: Jede natürliche Zahl lässt sich als Produkt von Primzahlen schreiben. Diese Darstellung ist bis auf die Reihenfolge der Faktoren eindeutig.

Beispiel: Die Zahl 260 kann wie folgt mit Primzahlen aufbaut werden:

260 = 2*2*5*13 = 22*5*13

Man nennt die Primzahlen, die in einer Produktdarstellung einer gegebenen Zahl vorkommen, auch Primfaktoren der Zahl.

Das Faktorisierungsproblem

Das Faktorisierungsproblem besteht darin, eine vorgegebene Zahl in ein Produkt aus Primfaktoren zu zerlegen.

Bei kleineren Zahlen kann man eine Primfaktorzerlegung oft direkt angeben. Bei größeren Zahlen sollte man systematisch vorgehen, um die Primfaktoren zu bestimmen. Bei noch größeren Zahlen ist es ratsam, die erforderlichen Berechnungen von einem Computer ausführen zu lassen - der kann das viel zuverlässiger und schneller.

Aufgabe 1

(a) Bei kleineren Zahlen kann man eine Primfaktorzerlegung oft direkt angeben. Bestimme eine Primfaktorzerlegung von n = 48 und n = 100.

(b) Bei größeren Zahlen sollte man systematisch vorgehen, um die Primfaktoren zu bestimmen. Bestimme eine Primfaktorzerlegung von n = 221 und n = 585.

(c) Entwickle zunächst einen Algorithmus zur Primfaktorzerlegung. Beschreibe in einem ersten Schritt in Worten das Verfahren, das du zur Primfaktorzerlegung von Zahlen benutzt. Beschreibe das Verfahren anschließend mit einem Struktogramm. Entwickle dann ein Programm zur Primfaktordarstellung. Hinweis: In Python bietet es sich an, eine Funktion primfaktoren(n) zu erstellen, die die Liste der Primfaktoren zurückgibt.

Ein einfaches Faktorisierungsverfahren

Ein einfaches Faktorisierungsverfahren basiert auf dem Verfahren der Probedivision: Wenn n die vorgegebene natürlich Zahl bezeichnet, dann dividiert man probeweise n der Reihe nach durch alle Zahlen von 2 aufwärts, bis die Division aufgeht oder die Wurzel aus n als Grenze erreicht wird. Wenn auf diese Weise ein Primfaktor gefunden wird, so wird er in einer Liste zur Verwaltung sämtlicher Primfaktoren aufgenommen. Die Ausgangszahl wird durch den Primfaktor divividiert und das Verfahren wird mit dem Quotienten analog durchgeführt. Das Verfahren endet, wenn die zu überprüfende Zahl eine Primzahl ist. Diese wird dann ebenfalls in der Liste der Primfaktoren aufgenommen.

Die folgende Übersicht verdeutlicht das Verfahren am Beispiel n = 51.

# Übergabe
n = 51
# Initialisierung
faktoren = []              {faktoren -> []}
z = n                      {z -> 51}
# Probedivisionen
z % 2 -> 1
z % 3 -> 0
# Aktualisierung
p = 3                      {p -> 3}
faktoren = faktoren + [p]  {faktoren -> [3]}
z = z // p                 {z -> 17}
# Probedivisionen
z % 2 -> 1
z % 3 -> 2
z % 4 -> 1
z % 5 -> 2
# Aktualisierung
p = z                      {p -> 17}
faktoren = faktoren + [p]  {faktoren -> [3, 17]}
z = z // p                 {z -> 1}
# Rückgabe
[3, 17]

Allgemein beschreiben lässt sich das Verfahren mit dem folgenden Algorithmus:

ALGORITHMUS primfaktoren(n):
    initialisiere die Liste faktoren: faktoren = []
    initialisiere die Hilfsvariable z: z = n
    SOLANGE z > 1:
        bestimme den kleinsten Primfaktor p von z mit Probedivisionen
        füge p in die Liste faktoren ein
        z = z // p
    Rückgabe: faktoren

Implementierung des Verfahrens

Das Verfahren kann wie folgt als Funktion in Python implementiert werden:

def primfaktoren(n):

    """ zerlegt eine Zahl in ihre Primfaktoren

    >>> primfaktoren(24)
    [2, 2, 2, 3]

    """

    faktoren = []
    z = n
    while z > 1:
        # bestimme den kleinsten Primfaktor p von z
        i = 2
        gefunden = False
        while i*i <= n and not gefunden:
            if z % i == 0:
                gefunden = True
                p = i
            else:
                i = i + 1
        if not gefunden:
            p = z
        # füge p in die Liste der Faktoren ein
        faktoren = faktoren + [p]
        z = z // p
    return faktoren

Aufgabe 2

Teste die Funktion primfaktoren(n). Wie zeigt sich, dass die Ausgangszahl eine Primzahl ist?

Aufgabe 3

Bestimme mit der Funktion primfaktoren(n) die Primfaktorzerlegung der beiden Zahlen 484639526894037745950720 und 565765434324543216797351. Was stellst du fest? Stelle eine Vermutung auf, warum es hier zu einem unterschiedlichen Laufzeitverhalten kommt.

Laufzeiten messen

Das folgende Testprogramm zeigt, wie man in Python die Laufzeit messen kann. Beachte, dass die Funktion primfaktoren(n) im Modul faktorisierung.py definiert ist.

from faktorisierung import primfaktoren

from time import *

n = 4563421773
t1 = clock()
ergebnis = primfaktoren(n)
t2 = clock()
t = t2 - t1

print("Zahl:        ", n)
print("Primfaktoren:", ergebnis)
print("Rechenzeit:  ", t)

Um Gesetzmäßigkeiten herauszufinden, sollte man beim Testen systematisch vorgehen.

Zunächst ist es günstig, sich auf Zahlen eines bestimmten Typs zu beschränken. Viele Probedivisionen muss das Verfahren durchführen, wenn die zu verarbeitende Zahl eine Primzahl ist. Dann muss die zu verarbeitende Zahl durch alle Zahlen von 2 bis zur Wurzel der Zahl dividiert werden.

Interessiert man sich für den Zusammenhang zwischen der Größe der vorgegebenen Zahl und der Laufzeit, sollte man die zu untersuchenden Zahlen systematisch vergrößern. Wir werden im Folgenden die vorgegebene Zahl immer (in etwa) um eine 10er-Potenz (d.h. eine Dezimalstelle) vergrößern. Als Testzahlen werden der Reihe nach die jeweils kleinsten Primzahlen genommen, die größer als die nächste 10er-Potenz ist.

from faktorisierung import primfaktoren

from time import *

testzahlen = [
11,
101,
1009,
10007,
100003,
1000003,
10000019,
100000007,
1000000007,
10000000019,
100000000003,
1000000000039,
10000000000037,
100000000000031,
1000000000000037,
10000000000000061,
100000000000000003,
1000000000000000003,
10000000000000000051,
100000000000000000039,
1000000000000000000117,
10000000000000000000009,
100000000000000000000117,
1000000000000000000000007,
10000000000000000000000013,
100000000000000000000000067,
1000000000000000000000000103,
10000000000000000000000000331,
100000000000000000000000000319]

for z in testzahlen:
    
    t1 = clock()
    ergebnis = primfaktoren(z)
    t2 = clock()
    t = t2 - t1

    print("Zahl:        ", z)
    print("Primfaktoren:", ergebnis)
    print("Rechenzeit:  ", t)
    print()

Aufgabe 4

Führe die Laufzeitmessungen durch. Entscheide selbst, wann du die Ausführung der Berechnungen abbrichst. Warum solltest du während der Messreihe keine anderen Programme starten und verwenden?

Gesetzmäßigkeiten herausfinden

Folgende Messergebnisse erhält man, wenn man systematische Laufzeitmessungen durchführt. Beachte, dass die Messergebnisse vom benutzten Rechner abhängen. Deine Messergebnisse werden sich daher wohl von den hier gezeigten unterscheiden.

>>> 
Zahl:         11
Primfaktoren: [11]
Rechenzeit:   8.93968367488e-06

Zahl:         101
Primfaktoren: [101]
Rechenzeit:   1.31301603975e-05

Zahl:         1009
Primfaktoren: [1009]
Rechenzeit:   2.6260320795e-05

Zahl:         10007
Primfaktoren: [10007]
Rechenzeit:   6.67682624468e-05

Zahl:         100003
Primfaktoren: [100003]
Rechenzeit:   0.000212317487278

Zahl:         1000003
Primfaktoren: [1000003]
Rechenzeit:   0.000668520719812

Zahl:         10000019
Primfaktoren: [10000019]
Rechenzeit:   0.00217876853064

Zahl:         100000007
Primfaktoren: [100000007]
Rechenzeit:   0.00702519454288

Zahl:         1000000007
Primfaktoren: [1000000007]
Rechenzeit:   0.0223148472781

Zahl:         10000000019
Primfaktoren: [10000000019]
Rechenzeit:   0.0867154903766

Zahl:         100000000003
Primfaktoren: [100000000003]
Rechenzeit:   0.286610449093

Zahl:         1000000000039
Primfaktoren: [1000000000039]
Rechenzeit:   0.906267137304

Zahl:         10000000000037
Primfaktoren: [10000000000037]
Rechenzeit:   2.88270213114

Zahl:         100000000000031
Primfaktoren: [100000000000031]
Rechenzeit:   9.1279123464

Zahl:         1000000000000037
Primfaktoren: [1000000000000037]
Rechenzeit:   28.5701070946

Zahl:         10000000000000061
Primfaktoren: [10000000000000061]
Rechenzeit:   91.2736900919

Aufgabe 5

Kannst du anhand der Zahlen bereits erste Zusammenhänge erkennen? Kannst du anhand der Zusammenhänge Prognosen erstellen, wie lange man wohl bis zum nächsten Ergebnis warten muss.

Gesetzmäßigkeiten beschreiben

Die Messergebnisse weisen (in etwa) folgende Regelmäßigkeit auf: Wenn man die Anzahl der Stellen der Ausgangszahl um 2 erhöht, dann erhöht sich die Laufzeit um den Faktor 10. Jede zusätzliche Stelle bei der Ausgangszahl führt also dazu, dass die Laufzeit mit dem Faktor √10 multipliziert wird.

Es handelt sich hier um ein exponentielles Wachstumsverhalten, das man mathematisch mit einer Exponentialfunktion beschreiben kann: Wenn k die Anzahl der Stellen der Ausgangszahl angibt, dann erhält man eine Laufzeit vom Typ L(k) = c*(√10)k mit einer Konstanten c.

Prognosen erstellen

Wie lange dauert es im ungünstigsten Fall, wenn man eine natürliche Zahl mit 100 Stellen in ihre Primfaktoren zerlegen möchte?

Wenn man das Laufzeitverhalten eines Algorithmus / Programms mit einer Gesetzmäßigkeit beschreiben kann, dann lässt sich diese Gesetzmäßigkeit benutzen, um Prognosen zu erstellen.

Beim vorliegenden Programm zur Primfaktorzerlegung benötigt man etwa 1 Sekunde, um eine 12-stellige Zahl im ungünstigten Fall zu faktorisieren. Wenn die Zahl z.B. 100 Stellen haben soll, also 88 Stellen mehr als eine 12-stellige Zahl, so benötigt man nach der gefundenen Gesetzmäßigkeit 1044-mal so lange wie bei der 12-stelligen Zahl - also etwa 1044 Sekunden. Das ist eine Zeit, die weit jenseits jeglicher Vorstellungsmöglichkeiten liegt.

Schnelle Faktorisierungsverfahren und RSA

Die Sicherheit des RSA-Verfahrens hängt davon ab, ob eine Primfaktorzerlegung effizient durchgeführt werden kann. Das oben beschriebene einfache Faktorisierungsverfahren leistet keine effiziente Primfaktorzerlegung. Es gibt durchaus Verfahren, die das Faktorisierungsproblem besser lösen als unser einfaches Verfahren. Es ist jedoch noch kein Algorithmus entwickelt worden, der das Faktorisierungsproblem mit nicht-exponentieller Zeitkomplexität löst. Alle bisher entwickelten Algorithmen sind demnach - im Fall großer Ausgangszahlen - praktisch nicht anwendbar. Ob es jemals Algorithmen geben wird, die das Faktorisierungsproblem mit nicht-exponentieller Zeitkomplexität lösen werden, ist nicht bekannt.

X

Fehler melden

X

Suche