Ich versuche mir selbst beizubringen, wie man die BigO-Notation für eine beliebige Funktion berechnet. Ich habe diese Funktion in einem Lehrbuch gefunden. Das Buch behauptet, dass die Funktion O (n 2 ) ist. Es gibt eine Erklärung, warum dies so ist, aber ich habe Mühe, dem zu folgen. Ich frage mich, ob jemand in der Lage sein könnte, mir die Mathematik dahinter zu zeigen, warum das so ist. Grundsätzlich verstehe ich, dass es etwas weniger als O (n 3 ) ist, aber ich konnte nicht unabhängig auf O (n 2 ) landen
Angenommen, wir erhalten drei Folgen von Zahlen, A, B und C. Wir gehen davon aus, dass keine einzelne Folge doppelte Werte enthält, sondern dass einige Zahlen in zwei oder drei Folgen vorkommen können. Das Dreiwegesatz-Disjunktitätsproblem besteht darin, zu bestimmen, ob der Schnittpunkt der drei Sequenzen leer ist, nämlich dass es kein Element x gibt, so dass x ≤ A, x ≤ B und x ≤ C.
Übrigens, das ist für mich kein Problem mit den Hausaufgaben - das Schiff ist vor Jahren gesegelt:), nur ich versuche schlauer zu werden.
def disjoint(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
if a == b: # only check C if we found match from A and B
for c in C:
if a == c # (and thus a == b == c)
return False # we found a common value
return True # if we reach this, sets are disjoint
[Bearbeiten] Nach dem Lehrbuch:
In der verbesserten Version sparen wir nicht nur Zeit, wenn wir Glück haben. Wir behaupten, dass die schlechteste Laufzeit für Disjunkte O (n 2 ) ist.
Die Erklärung des Buches, der ich nur schwer folgen kann, lautet:
Um die Gesamtlaufzeit zu berücksichtigen, untersuchen wir die Zeit, die für die Ausführung jeder Codezeile aufgewendet wurde. Die Verwaltung der for-Schleife über A erfordert O (n) Zeit. Die Verwaltung der for-Schleife über B macht insgesamt 0 (n 2 ) Zeit aus, da diese Schleife zu n verschiedenen Zeiten ausgeführt wird. Der Test a == b wird O (n 2 ) mal ausgewertet . Der Rest der aufgewendeten Zeit hängt davon ab, wie viele übereinstimmende (a, b) Paare existieren. Wie wir bemerkt haben, gibt es höchstens n solche Paare, und so verwenden die Verwaltung der Schleife über C und die Befehle innerhalb des Körpers dieser Schleife höchstens die Zeit O (n 2 ). Die Gesamtzeit beträgt O (n 2 ).
(Und um das richtig anzuerkennen ...) Das Buch ist: Datenstrukturen und Algorithmen in Python von Michael T. Goodrich et. alle, Wiley Publishing, pg. 135
[Bearbeiten] Eine Begründung; Unten ist der Code vor der Optimierung:
def disjoint1(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
for c in C:
if a == b == c:
return False # we found a common value
return True # if we reach this, sets are disjoint
Oben sehen Sie deutlich, dass dies O (n 3 ) ist, da jede Schleife in vollem Umfang ausgeführt werden muss. Das Buch würde behaupten, dass in dem vereinfachten Beispiel (als erstes gegeben) die dritte Schleife nur eine Komplexität von O (n 2 ) ist, daher lautet die Komplexitätsgleichung k + O (n 2 ) + O (n 2 ), was letztendlich ergibt O (n 2 ).
Ich kann zwar nicht beweisen, dass dies der Fall ist (daher die Frage), aber der Leser kann zustimmen, dass die Komplexität des vereinfachten Algorithmus mindestens geringer ist als die des Originals.
[Bearbeiten] Und um zu beweisen, dass die vereinfachte Version quadratisch ist:
if __name__ == '__main__':
for c in [100, 200, 300, 400, 500]:
l1, l2, l3 = get_random(c), get_random(c), get_random(c)
start = time.time()
disjoint1(l1, l2, l3)
print(time.time() - start)
start = time.time()
disjoint2(l1, l2, l3)
print(time.time() - start)
Erträge:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
Da die zweite Differenz gleich ist, ist die vereinfachte Funktion in der Tat quadratisch:
[Bearbeiten] Und noch ein Beweis:
Wenn ich den schlechtesten Fall annehme (A = B! = C),
if __name__ == '__main__':
for c in [10, 20, 30, 40, 50]:
l1, l2, l3 = range(0, c), range(0,c), range(5*c, 6*c)
its1 = disjoint1(l1, l2, l3)
its2 = disjoint2(l1, l2, l3)
print(f"iterations1 = {its1}")
print(f"iterations2 = {its2}")
disjoint2(l1, l2, l3)
ergibt:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
Beim zweiten Differenztest ist das Worst-Case-Ergebnis genau quadratisch.