Próbuję nauczyć się, jak obliczyć notację BigO dla dowolnej funkcji. Znalazłem tę funkcję w podręczniku. Książka potwierdza, że funkcją jest O (n 2 ). To wyjaśnia, dlaczego tak jest, ale staram się podążać. Zastanawiam się, czy ktoś mógłby mi pokazać matematykę, dlaczego tak jest. Zasadniczo rozumiem, że jest to coś mniejszego niż O (n 3 ), ale nie mogłem samodzielnie wylądować na O (n 2 )
Załóżmy, że podano nam trzy sekwencje liczb: A, B i C. Zakładamy, że żadna pojedyncza sekwencja nie zawiera zduplikowanych wartości, ale mogą istnieć pewne liczby, które występują w dwóch lub trzech sekwencjach. Problem rozłączności zestawu trójdrożnego polega na określeniu, czy przecięcie trzech sekwencji jest puste, a mianowicie, czy nie ma takiego elementu x, że x ∈ A, x ∈ B i x ∈ C.
Nawiasem mówiąc, nie jest to dla mnie problem w odrabianiu prac domowych - ten statek wypłynął lata temu:), tylko ja próbuję stać się mądrzejszy.
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
[Edytuj] Zgodnie z podręcznikiem:
W ulepszonej wersji nie tylko oszczędzamy czas, jeśli mamy szczęście. Twierdzimy, że najgorszym przypadkiem dla rozłączności jest O (n 2 ).
Wyjaśnienie książki, do którego staram się podążać, jest następujące:
Aby uwzględnić całkowity czas działania, sprawdzamy czas poświęcony na wykonanie każdego wiersza kodu. Zarządzanie pętlą for przez A wymaga czasu O (n). Zarządzanie pętlą for przez B stanowi łącznie O (n 2 ), ponieważ ta pętla jest wykonywana n razy. Test a == b jest oceniany O (n 2 ) razy. Reszta czasu zależy od liczby pasujących par (a, b). Jak zauważyliśmy, istnieje co najwyżej n takich par, a zatem zarządzanie pętlą nad C i polecenia w ciele tej pętli używają maksymalnie O (n 2 ) czasu. Całkowity czas spędzony to O (n 2 ).
(I przyznać należny kredyt ...) Książka jest: Struktury danych i algorytmy w Pythonie autorstwa Michaela T. Goodricha i in. wszystkie, Wiley Publishing, str. 135
[Edytuj] Uzasadnienie; Poniżej znajduje się kod przed optymalizacją:
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
Na powyższym widać wyraźnie, że jest to O (n 3 ), ponieważ każda pętla musi przebiegać w pełni. Książka twierdzi, że w uproszczonym przykładzie (podanym jako pierwszy) trzecia pętla jest tylko złożonością O (n 2 ), więc równanie złożoności przyjmuje postać k + O (n 2 ) + O (n 2 ), co ostatecznie daje O (n 2 ).
Chociaż nie mogę udowodnić, że tak jest (stąd pytanie), czytelnik może zgodzić się, że złożoność uproszczonego algorytmu jest co najmniej mniejsza niż w oryginale.
[Edytuj] Aby udowodnić, że uproszczona wersja jest kwadratowa:
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)
Wydajność:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
Ponieważ druga różnica jest równa, funkcja uproszczona jest rzeczywiście kwadratowa:
[Edytuj] A jeszcze więcej dowodów:
Jeśli założę najgorszy przypadek (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)
daje:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
W drugim teście różnic najgorszy wynik jest dokładnie kwadratowy.