Was ist Pseudopolynomzeit? Wie unterscheidet es sich von der Polynomzeit?

Antworten:

253

Um den Unterschied zwischen Polynomzeit und Pseudopolynomzeit zu verstehen, müssen wir zunächst formalisieren, was "Polynomzeit" bedeutet.

Die übliche Intuition für die Polynomzeit ist "Zeit O (n k ) für einige k". Zum Beispiel läuft die Auswahlsortierung in der Zeit O (n 2 ), die Polynomzeit ist, während die Brute-Force-Lösung TSP die Zeit O (n · n!) Benötigt , die keine Polynomzeit ist.

Diese Laufzeiten beziehen sich alle auf eine Variable n, die die Größe der Eingabe verfolgt. Bei der Auswahlsortierung bezieht sich n beispielsweise auf die Anzahl der Elemente im Array, während sich bei TSP n auf die Anzahl der Knoten im Diagramm bezieht. Um die Definition dessen, was "n" in diesem Zusammenhang tatsächlich bedeutet, zu standardisieren, definiert die formale Definition der Zeitkomplexität die "Größe" eines Problems wie folgt:

Die Größe der Eingabe für ein Problem ist die Anzahl der Bits, die zum Ausschreiben dieser Eingabe erforderlich sind.

Wenn die Eingabe in einen Sortieralgorithmus beispielsweise ein Array von 32-Bit-Ganzzahlen ist, beträgt die Größe der Eingabe 32n, wobei n die Anzahl der Einträge im Array ist. In einem Diagramm mit n Knoten und m Kanten kann die Eingabe als Liste aller Knoten angegeben werden, gefolgt von einer Liste aller Kanten, für die Ω (n + m) Bits erforderlich wären.

In Anbetracht dieser Definition lautet die formale Definition der Polynomzeit wie folgt:

Ein Algorithmus läuft in Polynomzeit, wenn seine Laufzeit für eine Konstante k O (x k ) ist, wobei x die Anzahl der dem Algorithmus gegebenen Eingabebits bezeichnet.

Bei der Arbeit mit Algorithmen, die Diagramme, Listen, Bäume usw. verarbeiten, stimmt diese Definition mehr oder weniger mit der herkömmlichen Definition überein. Angenommen, Sie haben einen Sortieralgorithmus, der Arrays mit 32-Bit-Ganzzahlen sortiert. Wenn Sie dazu so etwas wie eine Auswahlsortierung verwenden, ist die Laufzeit in Abhängigkeit von der Anzahl der Eingabeelemente im Array O (n 2 ). Aber wie entspricht n, die Anzahl der Elemente im Eingabearray, der Anzahl der Eingabebits? Wie bereits erwähnt, beträgt die Anzahl der Eingangsbits x = 32n. Wenn wir also die Laufzeit des Algorithmus in Form von x anstelle von n ausdrücken, erhalten wir, dass die Laufzeit O (x 2 ) ist und der Algorithmus daher in Polynomzeit ausgeführt wird.

Angenommen, Sie führen eine Tiefensuche in einem Diagramm durch, die die Zeit O (m + n) benötigt, wobei m die Anzahl der Kanten im Diagramm und n die Anzahl der Knoten ist. In welcher Beziehung steht dies zur Anzahl der angegebenen Eingabebits? Wenn wir annehmen, dass der Eingang als Adjazenzliste (eine Liste aller Knoten und Kanten) angegeben ist, beträgt die Anzahl der Eingangsbits, wie bereits erwähnt, x = Ω (m + n). Daher ist die Laufzeit O (x), sodass der Algorithmus in Polynomzeit ausgeführt wird.

Die Dinge brechen jedoch zusammen, wenn wir über Algorithmen sprechen, die mit Zahlen arbeiten. Betrachten wir das Problem des Testens, ob eine Zahl eine Primzahl ist oder nicht. Mit einer Zahl n können Sie mit dem folgenden Algorithmus testen, ob n eine Primzahl ist:

function isPrime(n):
    for i from 2 to n - 1:
        if (n mod i) = 0, return false
    return true

Was ist die zeitliche Komplexität dieses Codes? Nun, diese innere Schleife läuft O (n) Mal und macht jedes Mal etwas Arbeit, um n mod i zu berechnen (als wirklich konservative Obergrenze kann dies sicherlich in der Zeit O (n 3 ) erfolgen). Daher läuft dieser Gesamtalgorithmus in der Zeit O (n 4 ) und möglicherweise viel schneller.

Im Jahr 2004 veröffentlichten drei Informatiker einen Artikel mit dem Titel PRIMES is in P , der einen Polynom-Zeit-Algorithmus zum Testen der Primzahl einer Zahl enthält. Es wurde als wegweisendes Ergebnis angesehen. Also, was ist die große Sache? Haben wir dafür nicht bereits einen Polynom-Zeit-Algorithmus, nämlich den oben genannten?

Leider nicht. Denken Sie daran, dass die formale Definition der Zeitkomplexität über die Komplexität des Algorithmus als Funktion der Anzahl der Eingabebits spricht . Unser Algorithmus läuft in der Zeit O (n 4 ), aber was ist das als Funktion der Anzahl der Eingangsbits? Nun, das Ausschreiben der Zahl n erfordert O (log n) Bits. Wenn wir also x die Anzahl der Bits sein lassen, die zum Ausschreiben des Eingangs n erforderlich sind, ist die Laufzeit dieses Algorithmus tatsächlich O (2 4x ), was in x kein Polynom ist.

Dies ist das Herzstück der Unterscheidung zwischen Polynomzeit und Pseudopolynomzeit. Einerseits ist unser Algorithmus O (n 4 ), das wie ein Polynom aussieht, andererseits ist es unter der formalen Definition der Polynomzeit keine Polynomzeit.

Denken Sie an Folgendes, um eine Vorstellung davon zu bekommen, warum der Algorithmus kein Polynom-Zeit-Algorithmus ist. Angenommen, ich möchte, dass der Algorithmus viel Arbeit leisten muss. Wenn ich eine Eingabe wie diese schreibe:

10001010101011

Dann wird es im schlimmsten Fall einige Zeit dauern T, bis der Vorgang abgeschlossen ist. Wenn ich jetzt ein einzelnes Bit am Ende der Zahl hinzufüge , so:

100010101010111

Die Laufzeit beträgt jetzt (im schlimmsten Fall) 2T. Ich kann den Arbeitsaufwand des Algorithmus verdoppeln, indem ich nur noch ein Bit hinzufüge!

Ein Algorithmus wird in Pseudopolynomzeit ausgeführt, wenn die Laufzeit ein Polynom im numerischen Wert der Eingabe ist und nicht in der Anzahl der Bits, die zur Darstellung erforderlich sind. Unser Haupttestalgorithmus ist ein Pseudopolynomzeitalgorithmus, da er in der Zeit O (n 4 ) ausgeführt wird, aber kein Polynomzeitalgorithmus, da die Laufzeit in Abhängigkeit von der Anzahl der zum Ausschreiben der Eingabe erforderlichen Bits x O ist (2 4x ). Der Grund dafür, dass das Papier "PRIMES is in P" so bedeutend war, war, dass seine Laufzeit (ungefähr) O (log 12 n) betrug , was in Abhängigkeit von der Anzahl der Bits O (x 12 ) ist.

Warum ist das so wichtig? Nun, wir haben viele pseudopolynomiale Zeitalgorithmen zum Faktorisieren von ganzen Zahlen. Diese Algorithmen sind jedoch technisch gesehen Exponentialzeitalgorithmen. Dies ist sehr nützlich für die Kryptografie: Wenn Sie die RSA-Verschlüsselung verwenden möchten, müssen Sie darauf vertrauen können, dass wir Zahlen nicht einfach faktorisieren können. Indem Sie die Anzahl der Bits in den Zahlen auf einen großen Wert erhöhen (z. B. 1024 Bits), können Sie die Zeit, die der Pseudopolynom-Zeit-Factoring-Algorithmus benötigen muss, so groß machen, dass es völlig unmöglich wäre, die zu faktorisieren Zahlen. Wenn wir andererseits einen Polynom- Time-Factoring-Algorithmus finden, ist dies nicht unbedingt der Fall. Das Hinzufügen weiterer Bits kann dazu führen, dass die Arbeit um ein Vielfaches wächst, aber das Wachstum ist nur ein Polynomwachstum, kein exponentielles Wachstum.

In vielen Fällen sind pseudopolynomiale Zeitalgorithmen jedoch vollkommen in Ordnung, da die Zahlen nicht zu groß sind. Zum Beispiel hat die Zählsortierung die Laufzeit O (n + U), wobei U die größte Zahl im Array ist. Dies ist eine Pseudopolynomzeit (da für den numerischen Wert von U O (log U) -Bits zum Ausschreiben erforderlich sind, sodass die Laufzeit in der Eingabegröße exponentiell ist). Wenn wir U künstlich einschränken, damit U nicht zu groß ist (sagen wir, wenn wir U 2 sein lassen), dann ist die Laufzeit O (n), was tatsächlich die Polynomzeit ist. So funktioniert die Radix-Sortierung : Durch die bitweise Verarbeitung der Zahlen beträgt die Laufzeit jeder Runde O (n), sodass die Gesamtlaufzeit O (n log U) ist. Das ist eigentlich so Polynomzeit, da beim Ausschreiben von n Zahlen zum Sortieren Ω (n) Bits verwendet werden und der Wert von log U direkt proportional zur Anzahl der Bits ist, die zum Ausschreiben des Maximalwerts im Array erforderlich sind.

Hoffe das hilft!

templatetypedef
quelle
27
Dies sollte die Wikipedia-Erklärung sein ...
Sandro Meier
4
Warum wird isPrimedie Komplexität als O (n ^ 4) und nicht einfach als O (n) geschätzt? Ich verstehe es nicht Es sei denn, die Komplexität von n mod iist O (n ^ 3) .... was sicherlich nicht ist.
Fons
4
@Nobody Normalerweise betrachten wir die Kosten für das Modifizieren von zwei Zahlen als O (1), aber wenn Sie mit beliebig großen Zahlen arbeiten, steigen die Kosten für die Multiplikation in Abhängigkeit von der Größe der Zahlen selbst. Um äußerst konservativ zu sein, habe ich behauptet, dass Sie Modding mit einer Zahl berechnen können, die kleiner als n als O (n ^ 3) ist. Dies ist eine grobe Überzählung, aber immer noch nicht schlecht.
Templatetypedef
1
@ AndrewFlemming Es hängt davon ab, wie die Nummer im Speicher dargestellt wird. Ich nahm an, dass wir eine Standard-Binärdarstellung verwenden, bei der wir log_2 n Bits benötigen würden, um die Zahl darzustellen. Sie haben Recht, dass das Ändern der zugrunde liegenden Darstellung die Laufzeit in Abhängigkeit von der Größe der Eingabe ändert.
Templatetypedef
1
Die Auswahl von O (n ^ 3) für n mod iist zu konservativ. Das Timing von modist eine Funktion der Anzahl der Bits in n, nicht der nselbst, daher sollte es O ((log n) ^ 3) sein.
Dasblinkenlight
2

Pseudo-Polynom-Zeitkomplexität bedeutet Polynom im Wert / der Größe der Eingabe, aber exponentiell in der Größe der Eingabe.

Mit Größe ist die Anzahl der Bits gemeint, die zum Schreiben der Eingabe erforderlich sind.

Aus dem Pseudocode des Rucksacks können wir die zeitliche Komplexität als O (nW) ermitteln.

// Input:
// Values (stored in array v) 
// Weights (stored in array w)
// Number of distinct items (n) //
Knapsack capacity (W) 
for w from 0 to W 
    do   m[0, w] := 0 
end for  
for i from 1 to n do  
        for j from 0 to W do
               if j >= w[i] then 
                      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i]) 
              else 
                      m[i, j] := m[i-1, j]
              end if
       end for 
end for

Hier ist W jedoch in der Länge der Eingabe nicht polynomisch, was es zu einem Pseudopolynom macht.

Sei s die Anzahl der Bits, die zur Darstellung von W erforderlich sind

i.e. size of input= s =log(W) (log= log base 2)
-> 2^(s)=2^(log(W))
-> 2^(s)=W  (because  2^(log(x)) = x)

Nun ist running time of knapsack= O (nW) = O (n * 2 ^ s), was kein Polynom ist.

Adi Agarwal
quelle