Algorytmy i struktury danych Wykład 4 Algorytmy i ich analiza Janusz Szwabiński Plan wykładu: Co to jest algorytm? Analiza algorytmów motywacja Cele analizy Analiza eksperymentalna Studium przypadku 3SUM
Co to jest algorytm? Algorytm to skończony ciąg jasno zdefiniowanych czynności, koniecznych do wykonania pewnego rodzaju zadań. Innymi słowy to przepis na rozwiązanie problemu lub osiągnięcie jakiegoś celu. Istnieje kilka różnych metod zapisu algorytmu. Załóżmy, że naszym zadaniem jest obliczenie funkcji f(0) = 0 przy założeniu, że. f(x) = x x Słowny opis algorytmu 1. dla liczb ujemnych, więc, 2. dla liczb dodatnich, więc, x = 0 3. jeśli, to z definicji $f(0)=0. x = x f(x) = x/( x) = 1 x = x f(x) = x/x = 1 Opis słowny czasami da się w prosty sposób wyrazić wzorem matematycznym: f(x) = 1, 0, 1, x < 0 x = 0 x > 0 Lista kroków 1. Wczytaj wartość danej. 2. Jeśli, to. Zakończ algorytm. 3. Jeśli, to. Zakończ algorytm. x x > 0 f(x) = 1 x = 0 f(x) = 0 x < 0 f(x) = 1 4. Jeśli, to. Zakończ algorytm. Schemat blokowy Poszczególne elementy na powyższym schemacie mają następujące znaczenie:
Drzewo algorytmu Program komputerowy to nic innego jak algorytm zapisany w wybranym języku programowania. Powyższy algorytm zapisany w Pythonie mógłby mieć postać:
Dlaczego warto poznać algorytmy? cały obszar IT opiera się na algorytmach niektóre z nich są ponadczasowe: Euklides z Aleksandrii zajmował się badaniem algorytmów już w IV w p.n.e. (Źródło: Wikipedia) jego algorytm wyznaczania największego wspólnego dzielnika dwóch liczb (NWD) jest znany do dziś NWD jest stosowany w algorytmie RSA najpopularniejszym obecnie asymetrycznym algorytmie kryptograficznym z kluczem publicznym (https://pl.wikipedia.org/wiki/kryptografia_klucza_publicznego (https://pl.wikipedia.org/wiki/kryptografia_klucza_publicznego)) pozwalają rozwiązać zagadnienia, które inaczej pozostałyby nierozwiązane: problem komiwojażera, np. wyznaczenie najkrótszej trasy pozwalającej na zwiedzenie wszystkich stolic województw pozwalają stać się profesjonalnym programistą I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships. (Linus Torvalds, twórca Linuksa) stymulują intelektualnie przynoszą zysk <table class="image" width=50%> (Źródło: http://www.marketwatch.com)
Analiza algorytmów motywacja Często bywa tak, że ten sam algorytm zaimplementowany jest na wiele różnych sposobów. Nasuwa się wtedy pytanie, która z implementacji jest lepsza od pozostałych. Dla przykładu porównajmy dwa programy: In [1]: def sumofn(n): thesum = 0 for i in range(1,n+1): thesum = thesum + i return thesum print(sumofn(10)) 55 In [2]: def foo(tom): fred = 0 for bill in range(1,tom+1): barney = bill fred = fred + barney return fred print(foo(10)) 55 Mimo, że na pierwszy rzut oka tego nie widać, oba robią to samo sumują liczby od do, i na dodatek robią to w ten sam sposób. Mimo to powiemy, że pierwszy program jest lepszy ze względu na czytelność. Ogólnie rzecz biorąc, aby dokonać porównania między programami, musimy zdefiniować odpowiednie kryteria. Oprócz czytelności mogą to być: liczba operacji niezbędnych do wykonania "zasobożerność" wydajność czas wykonania 1 n Analiza algorytmów zajmuje się właśnie ich porównywaniem pod względem nakładów obliczeniowych niezbędnych do uzyskania rozwiązania. Wróćmy jeszcze raz do pierwszego z powyższych programów i zmodyfikujmy go tak, aby wyliczał jeszcze czas sumowania:
In [3]: import time def sumofn2(n): start = time.time() thesum = 0 for i in range(1,n+1): thesum = thesum + i end = time.time() return thesum,end-start Wyniki pięciu wywołań funkcji sumofn2dla n = 10000 są następujące: In [4]: for i in range(5): print("suma wynosi %d, czas wykonania: %10.7f sekund"%sumofn2(10000)) Suma wynosi 50005000, czas wykonania: 0.0010314 sekund Suma wynosi 50005000, czas wykonania: 0.0009410 sekund Suma wynosi 50005000, czas wykonania: 0.0009718 sekund Suma wynosi 50005000, czas wykonania: 0.0010254 sekund Suma wynosi 50005000, czas wykonania: 0.0009537 sekund Czasy wykonania poszczególnych wywołań różnią się nieznacznie od siebie (zależą od chwilowego obciążenia komputera), jednak rząd wielkości pozostaje ten sam. Zobaczmy, co stanie się, jeżeli zwiększymy o jeden rząd: n In [5]: for i in range(5): print("suma wynosi %d, czas wykonania: %10.7f sekund"%sumofn2(100000)) Suma wynosi 5000050000, czas wykonania: 0.0113173 sekund Suma wynosi 5000050000, czas wykonania: 0.0105736 sekund Suma wynosi 5000050000, czas wykonania: 0.0092735 sekund Suma wynosi 5000050000, czas wykonania: 0.0099654 sekund Suma wynosi 5000050000, czas wykonania: 0.0109329 sekund I znowu, czasy wykonania są dość podobne do siebie, jednak w porównaniu z poprzednim przykładem wzrosły mniej więcej dziesięciokrotnie. Dla jeszcze większego otrzymamy: n In [6]: for i in range(5): print("suma wynosi %d, czas wykonania: %10.7f sekund"%sumofn2(1000000)) Suma wynosi 500000500000, czas wykonania: 0.0880532 sekund Suma wynosi 500000500000, czas wykonania: 0.0663662 sekund Suma wynosi 500000500000, czas wykonania: 0.0571766 sekund Suma wynosi 500000500000, czas wykonania: 0.0511684 sekund Suma wynosi 500000500000, czas wykonania: 0.0486953 sekund
Widzimy, że czas wykonania ponownie wzrósł. Zauważmy teraz, że program sumofn2wylicza sumę częściową ciągu arytmetycznego o różnicy i wyrazie początkowym 1. Korzystając z własności ciągu sumę tę możemy wyliczyć przy pomocy wyrażenia: n In [7]: def sumofn3(n): start = time.time() thesum = n*(n+1)/2 end = time.time() return thesum,end-start i = i=1 n(n + 1) 2 r = 1 In [8]: for n in (10000,100000,1000000,10000000): print("suma wynosi %d, czas wykonania: %10.7f sekund"%sumofn3(n)) Suma wynosi 50005000, czas wykonania: 0.0000021 sekund Suma wynosi 5000050000, czas wykonania: 0.0000012 sekund Suma wynosi 500000500000, czas wykonania: 0.0000007 sekund Suma wynosi 50000005000000, czas wykonania: 0.0000007 sekund Analizując te wyniki dochodzimy do dwóch wniosków: 1. sumofn3wykonuje się dużo szybciej niż sumofn2 2. w przypadku sumofn3czas wykonania funkcji nieznacznie zależy od n Cele analizy przewidywanie wydajności algorytmów kiedy program się zakończy? ile pamięci będzie potrzebował? porównywanie algorytmów co zmienić, aby program działał szybciej? czy wprowadzenie zmiany spowoduje, że program zadziała szybciej? gwarancja poprawności algorytmów czy program się zakończy? czy dla prawidłowych danych zwróci prawidłowy wynik? zrozumienie algorytmów unikanie błędów wykonania
Analiza eksperymentalna obserwacja pewnych własności świata naturalnego, np. czasu pracy programu na komputerze opracowanie hipotezy (modelu), który jest zgodny z obserwacjami przewidywanie zdarzeń za pomocą opracowanej hipotezy, np. czasu pracy programu dla danych wejściowych o dużych rozmiarach lub czasu wykonania na innym komputerze weryfikacja przewidywań poprzez dalsze obserwacje walidacja poprzez powtarzanie weryfikacji do momentu, kiedy hipotezy i obserwacje są zgodne Zasady powtarzalność eksperymentów ktoś inny powinien być w stanie je przeprowadzić falsyfikowalność hipotez można wykazać fałszywość hipotezy przez wskazanie przypadku, który jej nie spełnia Studium przypadku 3SUM 3SUM (https://en.wikipedia.org/wiki/3sum (https://en.wikipedia.org/wiki/3sum)) to znany problem w teorii złożoności obliczeniowej: czy dla danego zbioru liczb N naturalnych istnieją elementy a, b i c z tego zbioru takie, że Jeśli tak, to ile ich jest? Dla zainteresowanych (jeden z nierozwiązanych problemów w informatyce): Czy istnieje algorytm rozwiązujący zagadnienie 3SUM w czasie ϵ > 0? a + b + c = 0 O( n 2 ϵ ) dla pewnego In [9]: import random In [13]: tab = [] for i in range(8): tab.append(random.randint(-50,50)) In [14]: tab Out[14]: [-41, -45, -22, 23, -30, 11, 30, -25] Metoda naiwna:
In [15]: count = 0 for i in range(len(tab)): for j in range(i+1, len(tab)): for k in range(j+1, len(tab)): if (tab[i]+tab[j]+tab[k]==0): count = count + 1 print(tab[i],tab[j],tab[k]) print("in total: %d"%count) -41 11 30 In total: 1 Krok 1 Program Stwórzmy program w oparciu o powyższy algorytm. Program ten powinien wczytywać wielkość zbioru liczb z linii poleceń: In [18]: %%writefile 3sum.py """3SUM problem solved with brute-force algorithm""" def counttriplets(tab): count = 0 triplets = [] for i in range(len(tab)): for j in range(i+1, len(tab)): for k in range(j+1, len(tab)): if (tab[i]+tab[j]+tab[k]==0): count = count + 1 triplets.append((tab[i],tab[j],tab[k])) return triplets if name == " main ": import sys import random n = int(sys.argv[1]) tab = [] for i in range(n): tab.append(random.randint(-50,50)) triplets = counttriplets(tab) if triplets: for t in triplets: print(t) print("in total: %d"%len(triplets)) else: print("no triplets found") Overwriting 3sum.py
In [19]:!python3 3sum.py 10 (-4, 12, -8) (4, 12, -16) In total: 2 Krok 2 pomiar czasu pracy programu Metoda 1 pomiar ręczny możliwe do przeprowadzenia, ale uciążliwe w zasadzie nie po to używamy komputerów Metoda 2 pomiar z poziomu systemu operacyjnego In [20]:!time python3 3sum.py 10 No triplets found real user sys 0m0.037s 0m0.032s 0m0.004s
In [21]:!time python3 3sum.py 20 (32, -3, -29) (32, -45, 13) (32, -49, 17) (-3, -41, 44) (-3, 37, -34) (-3, 37, -34) (-25, 13, 12) (-13, 19, -6) (45, -39, -6) (-49, 12, 37) (12, 17, -29) In total: 11 real user sys 0m0.035s 0m0.028s 0m0.004s
In [22]:!time python3 3sum.py 100
(-31, 38, -7) (-31, 38, -7) (-31, -17, 48) (-31, -1, 32) (-31, 49, -18) (-31, 37, -6) (-31, 30, 1) (-31, 9, 22) (-31, -5, 36) (-31, 36, -5) (-31, 37, -6) (-31, -8, 39) (-31, 25, 6) (-31, 25, 6) (-31, 22, 9) (-31, 14, 17) (-31, 25, 6) (-31, 25, 6) (-31, 17, 14) (-31, 37, -6) (-31, 37, -6) (-31, -1, 32) (-31, 37, -6) (-31, -10, 41) (-31, 0, 31) (-31, 31, 0) (-31, 49, -18) (-31, 37, -6) (-12, -34, 46) (-12, -34, 46) (-12, -20, 32) (-12, -27, 39) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, 8, 4) (-12, 3, 9) (-12, 3, 9) (-12, -29, 41) (-12, -17, 29) (-12, -17, 29) (-12, -27, 39) (-12, 49, -37) (-12, 49, -37) (-12, 37, -25) (-12, 30, -18) (-12, 9, 3) (-12, 9, 3) (-12, 48, -36) (-12, 48, -36) (-12, -5, 17) (-12, 20, -8) (-12, 37, -25) (-12, -37, 49) (-12, 12, 0) (-12, 12, 0) (-12, -8, 20) (-12, 22, -10)
(-12, 14, -2) (-12, 3, 9) (-12, -2, 14) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, -25, 37) (-12, 17, -5) (-12, 8, 4) (-12, 41, -29) (-12, -27, 39) (-12, 9, 3) (-12, 6, 6) (-12, 12, 0) (-12, 12, 0) (-12, 49, -37) (-12, -27, 39) (-34, 38, -4) (-34, 38, -4) (-34, 38, -4) (-34, 8, 26) (-34, 3, 31) (-34, -7, 41) (-34, 30, 4) (-34, 40, -6) (-34, 9, 25) (-34, 9, 25) (-34, -5, 39) (-34, 36, -2) (-34, 44, -10) (-34, -7, 41) (-34, 20, 14) (-34, 20, 14) (-34, 12, 22) (-34, -8, 42) (-34, -8, 42) (-34, 25, 9) (-34, 22, 12) (-34, 14, 20) (-34, 3, 31) (-34, 25, 9) (-34, 34, 0) (-34, 34, 0) (-34, -5, 39) (-34, 20, 14) (-34, 8, 26) (-34, 44, -10) (-34, 31, 3) (46, -25, -21) (46, -29, -17) (46, -17, -29) (46, -5, -41) (46, -36, -10) (46, -46, 0) (46, -46, 0) (46, -40, -6) (46, -21, -25) (46, -41, -5) (46, -10, -36) (46, -50, 4) (46, -6, -40)
(38, -20, -18) (38, 8, -46) (38, 3, -41) (38, -7, -31) (38, -17, -21) (38, -1, -37) (38, -1, -37) (38, -5, -33) (38, -7, -31) (38, -36, -2) (38, -46, 8) (38, -37, -1) (38, 12, -50) (38, -41, 3) (38, -41, 3) (38, -2, -36) (38, -5, -33) (38, -1, -37) (38, -48, 10) (38, -48, 10) (38, -50, 12) (50, -27, -23) (50, -25, -25) (50, -7, -43) (50, -7, -43) (50, -29, -21) (50, -17, -33) (50, -27, -23) (50, -43, -7) (50, -43, -7) (50, -46, -4) (50, -46, -4) (50, -46, -4) (50, -40, -10) (50, -21, -29) (50, -2, -48) (50, -10, -40) (50, -23, -27) (50, -23, -27) (50, -50, 0) (50, -50, 0) (-20, 8, 12) (-20, 8, 12) (-20, 3, 17) (-20, -29, 49) (-20, -29, 49) (-20, -17, 37) (-20, -17, 37) (-20, -17, 37) (-20, -17, 37) (-20, -17, 37) (-20, -17, 37) (-20, 49, -29) (-20, 30, -10) (-20, -5, 25) (-20, -5, 25) (-20, 20, 0) (-20, 20, 0) (-20, 12, 8) (-20, 25, -5) (-20, -21, 41)
(-20, 22, -2) (-20, 14, 6) (-20, 14, 6) (-20, 3, 17) (-20, 25, -5) (-20, 17, 3) (-20, 20, 0) (-20, 20, 0) (-20, 8, 12) (-20, 10, 10) (-20, 6, 14) (-20, 6, 14) (-20, 49, -29) (-20, -6, 26) (-27, 7, 20) (-27, 7, 20) (-27, -7, 34) (-27, -17, 44) (-27, -17, 44) (-27, 37, -10) (-27, 48, -21) (-27, -5, 32) (-27, -7, 34) (-27, 20, 7) (-27, 37, -10) (-27, -4, 31) (-27, -2, 29) (-27, -2, 29) (-27, 17, 10) (-27, 17, 10) (-27, -5, 32) (-27, 37, -10) (-27, 1, 26) (-27, 20, 7) (-27, 37, -10) (-27, 37, -10) (-27, -10, 37) (-27, -23, 50) (-27, -4, 31) (-27, 31, -4) (-25, 8, 17) (-25, 3, 22) (-25, -7, 32) (-25, -17, 42) (-25, -17, 42) (-25, -1, 26) (-25, 30, -5) (-25, 30, -5) (-25, 48, -23) (-25, -7, 32) (-25, 25, 0) (-25, 25, 0) (-25, -21, 46) (-25, 22, 3) (-25, 22, 3) (-25, 25, 0) (-25, 25, 0) (-25, -4, 29) (-25, -4, 29) (-25, -25, 50) (-25, 17, 8)
(-25, 29, -4) (-25, 29, -4) (-25, -1, 26) (-25, -4, 29) (-25, 31, -6) (-25, 29, -4) (8, -7, -1) (8, -7, -1) (8, -17, 9) (8, -17, 9) (8, -1, -7) (8, 40, -48) (8, -7, -1) (8, -37, 29) (8, -37, 29) (8, -8, 0) (8, -8, 0) (8, -40, 32) (8, 25, -33) (8, 25, -33) (8, -4, -4) (8, -4, -4) (8, -2, -6) (8, -25, 17) (8, 42, -50) (8, 29, -37) (8, 10, -18) (8, -50, 42) (8, 10, -18) (8, -4, -4) (8, 29, -37) (8, 32, -40) (3, 7, -10) (3, -7, 4) (3, -29, 26) (3, -17, 14) (3, -17, 14) (3, -1, -2) (3, -43, 40) (3, 37, -40) (3, 37, -40) (3, 30, -33) (3, 40, -43) (3, -7, 4) (3, 20, -23) (3, 37, -40) (3, 37, -40) (3, -37, 34) (3, -40, 37) (3, -40, 37) (3, -40, 37) (3, -40, 37) (3, 22, -25) (3, 3, -6) (3, -4, 1) (3, -2, -1) (3, 34, -37) (3, 37, -40) (3, 1, -4) (3, 1, -4) (3, 20, -23)
(3, 37, -40) (3, 37, -40) (3, -35, 32) (3, -10, 7) (3, 37, -40) (3, 3, -6) (3, 26, -29) (7, -7, 0) (7, -7, 0) (7, -29, 22) (7, -17, 10) (7, -17, 10) (7, -27, 20) (7, -27, 20) (7, -1, -6) (7, -43, 36) (7, 30, -37) (7, 30, -37) (7, -5, -2) (7, 36, -43) (7, -7, 0) (7, -7, 0) (7, 20, -27) (7, 20, -27) (7, -36, 29) (7, -36, 29) (7, -46, 39) (7, -8, 1) (7, -21, 14) (7, -21, 14) (7, 22, -29) (7, -41, 34) (7, 3, -10) (7, -2, -5) (7, 20, -27) (7, 20, -27) (7, 29, -36) (7, -1, -6) (7, -48, 41) (7, -33, 26) (7, -10, 3) (7, 29, -36) (-7, -29, 36) (-7, -27, 34) (-7, -1, 8) (-7, -43, 50) (-7, 30, -23) (-7, 40, -33) (-7, 9, -2) (-7, 48, -41) (-7, -5, 12) (-7, -5, 12) (-7, 36, -29) (-7, 44, -37) (-7, 44, -37) (-7, -43, 50) (-7, -7, 14) (-7, -7, 14) (-7, -37, 44) (-7, 12, -5) (-7, 25, -18)
(-7, 3, 4) (-7, 25, -18) (-7, -2, 9) (-7, 34, -27) (-7, 34, -27) (-7, -25, 32) (-7, 17, -10) (-7, -5, 12) (-7, 1, 6) (-7, 1, 6) (-7, 42, -35) (-7, 8, -1) (-7, 44, -37) (-7, -35, 42) (-7, 0, 7) (-7, 4, 3) (-7, 0, 7) (-29, -17, 46) (-29, -1, 30) (-29, 37, -8) (-29, 30, -1) (-29, 9, 20) (-29, 9, 20) (-29, -5, 34) (-29, 36, -7) (-29, 20, 9) (-29, 37, -8) (-29, 12, 17) (-29, -8, 37) (-29, -8, 37) (-29, -8, 37) (-29, -8, 37) (-29, 25, 4) (-29, -21, 50) (-29, 22, 7) (-29, 3, 26) (-29, 25, 4) (-29, -2, 31) (-29, 34, -5) (-29, 17, 12) (-29, 20, 9) (-29, 29, 0) (-29, 29, 0) (-29, -10, 39) (-29, 0, 29) (-29, 29, 0) (-29, 3, 26) (-17, -27, 44) (-17, -27, 44) (-17, 40, -23) (-17, 9, 8) (-17, 48, -31) (-17, -5, 22) (-17, 44, -27) (-17, 44, -27) (-17, -8, 25) (-17, -8, 25) (-17, 22, -5) (-17, 14, 3) (-17, 14, 3) (-17, 3, 14)
(-17, -25, 42) (-17, -25, 42) (-17, 46, -29) (-17, 17, 0) (-17, 17, 0) (-17, 8, 9) (-17, 44, -27) (-17, 44, -27) (-17, -33, 50) (-17, 10, 7) (-17, 10, 7) (-17, 3, 14) (-27, 37, -10) (-27, 48, -21) (-27, -5, 32) (-27, -7, 34) (-27, 20, 7) (-27, 37, -10) (-27, -4, 31) (-27, -2, 29) (-27, -2, 29) (-27, 17, 10) (-27, 17, 10) (-27, -5, 32) (-27, 37, -10) (-27, 1, 26) (-27, 20, 7) (-27, 37, -10) (-27, 37, -10) (-27, -10, 37) (-27, -23, 50) (-27, -4, 31) (-27, 31, -4) (-1, 49, -48) (-1, -43, 44) (-1, -43, 44) (-1, 37, -36) (-1, 37, -36) (-1, 30, -29) (-1, 9, -8) (-1, -5, 6) (-1, -5, 6) (-1, 36, -35) (-1, 44, -43) (-1, -43, 44) (-1, -7, 8) (-1, 37, -36) (-1, 37, -36) (-1, -36, 37) (-1, -36, 37) (-1, -36, 37) (-1, -36, 37) (-1, -8, 9) (-1, -40, 41) (-1, -31, 32) (-1, -21, 22) (-1, -41, 42) (-1, -41, 42) (-1, 3, -2) (-1, -2, 3) (-1, 34, -33)
(-1, -25, 26) (-1, -5, 6) (-1, -5, 6) (-1, 37, -36) (-1, 1, 0) (-1, 1, 0) (-1, 37, -36) (-1, 37, -36) (-1, -48, 49) (-1, 41, -40) (-1, 37, -36) (-1, -6, 7) (49, -43, -6) (49, -43, -6) (49, -8, -41) (49, -31, -18) (49, 1, -50) (49, -1, -48) (-43, 37, 6) (-43, 37, 6) (-43, 40, 3) (-43, 40, 3) (-43, 9, 34) (-43, 48, -5) (-43, 48, -5) (-43, 36, 7) (-43, 44, -1) (-43, -7, 50) (-43, 37, 6) (-43, 37, 6) (-43, 12, 31) (-43, 14, 29) (-43, 14, 29) (-43, 34, 9) (-43, 17, 26) (-43, 37, 6) (-43, 37, 6) (-43, 1, 42) (-43, 1, 42) (-43, 37, 6) (-43, 37, 6) (-43, 29, 14) (-43, -1, 44) (-43, 37, 6) (-43, 37, 6) (-43, 6, 37) (-43, 12, 31) (-43, 4, 39) (-43, 6, 37) (-43, 49, -6) (-43, 29, 14) (37, 9, -46) (37, -43, 6) (37, -43, 6) (37, -36, -1) (37, -46, 9) (37, -37, 0) (37, -37, 0) (37, -8, -29) (37, -40, 3) (37, -40, 3)
(37, -31, -6) (37, -41, 4) (37, 3, -40) (37, -4, -33) (37, -2, -35) (37, -1, -36) (37, -33, -4) (37, -33, -4) (37, -10, -27) (37, -10, -27) (37, 0, -37) (37, 3, -40) (37, -37, 0) (30, -5, -25) (30, -7, -23) (30, 20, -50) (30, -36, 6) (30, -36, 6) (30, -37, 7) (30, -40, 10) (30, -40, 10) (30, -31, 1) (30, 3, -33) (30, -25, -5) (30, 20, -50) (30, -1, -29) (30, -33, 3) (30, 10, -40) (30, 10, -40) (30, 6, -36) (30, 6, -36) (30, -37, 7) (40, -5, -35) (40, -43, 3) (40, -43, 3) (40, -7, -33) (40, -36, -4) (40, -36, -4) (40, -36, -4) (40, -46, 6) (40, -46, 6) (40, -40, 0) (40, -40, 0) (40, -41, 1) (40, -4, -36) (40, -5, -35) (40, 8, -48) (40, 10, -50) (40, -50, 10) (40, -4, -36) (40, 0, -40) (40, -36, -4) (40, 0, -40) (9, -5, -4) (9, -5, -4) (9, -5, -4) (9, -43, 34) (9, -7, -2) (9, 20, -29) (9, 37, -46) (9, -46, 37)
(9, -46, 37) (9, -46, 37) (9, -46, 37) (9, 12, -21) (9, -8, -1) (9, -40, 31) (9, -31, 22) (9, -21, 12) (9, -41, 32) (9, 14, -23) (9, -4, -5) (9, -5, -4) (9, -5, -4) (9, 1, -10) (9, 20, -29) (9, -48, 39) (9, -35, 26) (9, -23, 14) (9, 41, -50) (9, 9, -18) (9, 31, -40) (48, -5, -43) (48, -43, -5) (48, -7, -41) (48, -46, -2) (48, -8, -40) (48, -8, -40) (48, -21, -27) (48, -21, -27) (48, -25, -23) (48, -48, 0) (48, -48, 0) (-5, 36, -31) (-5, -7, 12) (-5, -7, 12) (-5, -36, 41) (-5, -37, 42) (-5, -37, 42) (-5, -21, 26) (-5, -41, 46) (-5, -4, 9) (-5, -2, 7) (-5, 34, -29) (-5, -5, 10) (-5, -5, 10) (-5, 1, 4) (-5, 42, -37) (-5, -1, 6) (-5, -1, 6) (-5, 41, -36) (-5, -27, 32) (-5, 9, -4) (-5, 9, -4) (-5, 32, -27) (-5, -37, 42) (36, -43, 7) (36, -7, -29) (36, -36, 0) (36, -36, 0) (36, -46, 10) (36, -46, 10)
(36, -37, 1) (36, 12, -48) (36, -40, 4) (36, -31, -5) (36, 14, -50) (36, 1, -37) (36, -1, -35) (36, -48, 12) (36, -50, 14) (36, 0, -36) (36, 4, -40) (36, -36, 0) (44, -43, -1) (44, -7, -37) (44, -7, -37) (44, -36, -8) (44, -8, -36) (44, -40, -4) (44, -40, -4) (44, -40, -4) (44, -21, -23) (44, -4, -40) (44, -48, 4) (44, -50, 6) (44, -50, 6) (44, -4, -40) (44, -4, -40) (-43, -7, 50) (-43, 37, 6) (-43, 37, 6) (-43, 12, 31) (-43, 14, 29) (-43, 14, 29) (-43, 34, 9) (-43, 17, 26) (-43, 37, 6) (-43, 37, 6) (-43, 1, 42) (-43, 1, 42) (-43, 37, 6) (-43, 37, 6) (-43, 29, 14) (-43, -1, 44) (-43, 37, 6) (-43, 37, 6) (-43, 6, 37) (-43, 12, 31) (-43, 4, 39) (-43, 6, 37) (-43, 49, -6) (-43, 29, 14) (-7, -37, 44) (-7, 12, -5) (-7, 25, -18) (-7, 3, 4) (-7, 25, -18) (-7, -2, 9) (-7, 34, -27) (-7, 34, -27) (-7, -25, 32) (-7, 17, -10)
(-7, -5, 12) (-7, 1, 6) (-7, 1, 6) (-7, 42, -35) (-7, 8, -1) (-7, 44, -37) (-7, -35, 42) (-7, 0, 7) (-7, 4, 3) (-7, 0, 7) (20, -46, 26) (20, -37, 17) (20, -40, 20) (20, -21, 1) (20, 3, -23) (20, -2, -18) (20, 17, -37) (20, 20, -40) (20, -23, 3) (20, -27, 7) (20, 9, -29) (20, -27, 7) (37, -36, -1) (37, -46, 9) (37, -37, 0) (37, -37, 0) (37, -8, -29) (37, -40, 3) (37, -40, 3) (37, -31, -6) (37, -41, 4) (37, 3, -40) (37, -4, -33) (37, -2, -35) (37, -1, -36) (37, -33, -4) (37, -33, -4) (37, -10, -27) (37, -10, -27) (37, 0, -37) (37, 3, -40) (37, -37, 0) (-36, -8, 44) (-36, 22, 14) (-36, 22, 14) (-36, 46, -10) (-36, -5, 41) (-36, 37, -1) (-36, 42, -6) (-36, 37, -1) (-36, 29, 7) (-36, -1, 37) (-36, -1, 37) (-36, 10, 26) (-36, 10, 26) (-36, 4, 32) (-36, 29, 7) (-36, -6, 42) (-46, 12, 34) (-46, 14, 32) (-46, -4, 50)
(-46, 34, 12) (-46, 46, 0) (-46, 46, 0) (-46, 17, 29) (-46, 17, 29) (-46, 37, 9) (-46, 20, 26) (-46, 42, 4) (-46, 37, 9) (-46, 37, 9) (-46, 50, -4) (-46, 50, -4) (-46, 9, 37) (-46, 4, 42) (-46, 32, 14) (-46, 39, 7) (-37, 12, 25) (-37, 12, 25) (-37, 25, 12) (-37, 3, 34) (-37, 25, 12) (-37, -4, 41) (-37, -2, 39) (-37, 34, 3) (-37, 17, 20) (-37, -5, 42) (-37, -5, 42) (-37, 37, 0) (-37, 37, 0) (-37, 8, 29) (-37, 8, 29) (-37, 37, 0) (-37, 37, 0) (-37, 37, 0) (-37, 37, 0) (-37, 41, -4) (-37, 41, -4) (-37, 6, 31) (-37, 0, 37) (-37, 31, 6) (-37, 37, 0) (12, -8, -4) (12, -8, -4) (12, -8, -4) (12, 25, -37) (12, -21, 9) (12, -41, 29) (12, -41, 29) (12, 25, -37) (12, -2, -10) (12, 17, -29) (12, 6, -18) (12, 6, -18) (-8, -31, 39) (-8, -21, 29) (-8, -21, 29) (-8, -41, 49) (-8, 14, -6) (-8, -4, 12) (-8, -2, 10) (-8, -2, 10)
(-8, 37, -29) (-8, 1, 7) (-8, 8, 0) (-8, 8, 0) (-8, 37, -29) (-8, -1, 9) (-8, 37, -29) (-8, 44, -36) (-8, -33, 41) (-8, -23, 31) (-8, -4, 12) (-8, 12, -4) (-8, 37, -29) (-8, 14, -6) (-8, 26, -18) (-40, 14, 26) (-40, 3, 37) (-40, 3, 37) (-40, 3, 37) (-40, 3, 37) (-40, -4, 44) (-40, -2, 42) (-40, -2, 42) (-40, 34, 6) (-40, 34, 6) (-40, 46, -6) (-40, 37, 3) (-40, 1, 39) (-40, 8, 32) (-40, 37, 3) (-40, -1, 41) (-40, 37, 3) (-40, 44, -4) (-40, 44, -4) (-40, -10, 50) (-40, 9, 31) (-40, 37, 3) (-40, 14, 26) (-31, 25, 6) (-31, 25, 6) (-31, 22, 9) (-31, 14, 17) (-31, 25, 6) (-31, 25, 6) (-31, 17, 14) (-31, 37, -6) (-31, 37, -6) (-31, -1, 32) (-31, 37, -6) (-31, -10, 41) (-31, 0, 31) (-31, 31, 0) (-31, 49, -18) (-31, 37, -6) (25, -21, -4) (25, -21, -4) (25, -21, -4) (25, 25, -50) (25, -2, -23) (25, -25, 0) (25, -25, 0)
(25, 8, -33) (25, -35, 10) (25, -35, 10) (25, 12, -37) (25, 4, -29) (-21, 22, -1) (-21, 14, 7) (-21, 25, -4) (-21, 25, -4) (-21, 25, -4) (-21, -25, 46) (-21, 17, 4) (-21, -5, 26) (-21, 1, 20) (-21, 44, -23) (-21, -10, 31) (-21, 50, -29) (-21, 9, 12) (-21, 14, 7) (-21, 39, -18) (22, 14, -36) (22, 3, -25) (22, -4, -18) (22, -25, 3) (22, 1, -23) (22, -48, 26) (22, -4, -18) (22, -36, 14) (22, -4, -18) (22, -29, 7) (-41, 34, 7) (-41, 46, -5) (-41, 37, 4) (-41, 42, -1) (-41, 37, 4) (-41, 29, 12) (-41, -1, 42) (-41, 37, 4) (-41, 10, 31) (-41, 41, 0) (-41, 41, 0) (-41, 9, 32) (-41, 10, 31) (-41, 12, 29) (-41, 4, 37) (14, -4, -10) (14, 34, -48) (14, -10, -4) (14, -10, -4) (14, -23, 9) (14, 4, -18) (14, -40, 26) (3, -4, 1) (3, -2, -1) (3, 34, -37) (3, 37, -40) (3, 1, -4) (3, 1, -4) (3, 20, -23) (3, 37, -40) (3, 37, -40)
(3, -35, 32) (3, -10, 7) (3, 37, -40) (3, 3, -6) (3, 26, -29) (25, -2, -23) (25, -25, 0) (25, -25, 0) (25, 8, -33) (25, -35, 10) (25, -35, 10) (25, 12, -37) (25, 4, -29) (-4, -2, 6) (-4, -2, 6) (-4, -25, 29) (-4, -25, 29) (-4, -5, 9) (-4, 37, -33) (-4, 1, 3) (-4, 8, -4) (-4, 8, -4) (-4, 37, -33) (-4, 37, -33) (-4, 44, -40) (-4, -35, 39) (-4, -33, 37) (-4, -10, 14) (-4, 10, -6) (-4, 41, -37) (-4, -27, 31) (-4, 10, -6) (-4, 0, 4) (-4, 4, 0) (-4, 31, -27) (-2, -5, 7) (-2, 37, -35) (-2, 20, -18) (-2, 42, -40) (-2, 8, -6) (-2, 37, -35) (-2, 29, -27) (-2, 29, -27) (-2, -1, 3) (-2, 37, -35) (-2, -48, 50) (-2, -35, 37) (-2, -10, 12) (-2, -27, 29) (-2, -4, 6) (-2, -4, 6) (-2, 6, -4) (-2, 31, -29) (-2, 6, -4) (-2, 29, -27) (-2, -37, 39) (-2, -40, 42) (34, -5, -29) (34, 1, -35) (34, -1, -33) (34, -48, 14)
(34, 6, -40) (34, 6, -40) (34, 3, -37) (-25, 17, 8) (-25, 29, -4) (-25, 29, -4) (-25, -1, 26) (-25, -4, 29) (-25, 31, -6) (-25, 29, -4) (46, -10, -36) (46, -50, 4) (46, -6, -40) (17, 1, -18) (17, 20, -37) (17, -48, 31) (17, 10, -27) (17, 10, -27) (17, -23, 6) (17, -23, 6) (17, -27, 10) (17, 10, -27) (17, 12, -29) (-5, 1, 4) (-5, 42, -37) (-5, -1, 6) (-5, -1, 6) (-5, 41, -36) (-5, -27, 32) (-5, 9, -4) (-5, 9, -4) (-5, 32, -27) (-5, -37, 42) (37, -1, -36) (37, -33, -4) (37, -33, -4) (37, -10, -27) (37, -10, -27) (37, 0, -37) (37, 3, -40) (37, -37, 0) (1, -1, 0) (1, -1, 0) (1, -33, 32) (1, -10, 9) (1, -27, 26) (1, -50, 49) (1, -4, 3) (1, 3, -4) (1, -27, 26) (1, 39, -40) (20, -23, 3) (20, -27, 7) (20, 9, -29) (20, -27, 7) (42, 8, -50) (42, -48, 6) (42, -48, 6) (42, -36, -6) (8, 29, -37) (8, 10, -18)
(8, -50, 42) (8, 10, -18) (8, -4, -4) (8, 29, -37) (8, 32, -40) (37, -1, -36) (37, -33, -4) (37, -33, -4) (37, -10, -27) (37, -10, -27) (37, 0, -37) (37, 3, -40) (37, -37, 0) (29, -35, 6) (29, -35, 6) (29, -33, 4) (29, -23, -6) (29, 0, -29) (29, -36, 7) (29, 0, -29) (-1, 37, -36) (-1, -48, 49) (-1, 41, -40) (-1, 37, -36) (-1, -6, 7) (37, -33, -4) (37, -33, -4) (37, -10, -27) (37, -10, -27) (37, 0, -37) (37, 3, -40) (37, -37, 0) (44, -48, 4) (44, -50, 6) (44, -50, 6) (44, -4, -40) (44, -4, -40) (-48, 41, 7) (-48, 9, 39) (-48, 6, 42) (-48, 6, 42) (-35, 41, -6) (-35, 9, 26) (-35, -4, 39) (-35, 6, 29) (-35, 4, 31) (-35, 6, 29) (-35, 32, 3) (-35, 39, -4) (-33, -4, 37) (-33, 4, 29) (-33, 37, -4) (-33, 39, -6) (-33, 26, 7) (-10, 10, 0) (-10, 10, 0) (-10, -27, 37) (-10, 50, -40) (-10, 10, 0) (-10, 10, 0) (-10, -4, 14)
(-10, 6, 4) (-10, 4, 6) (-10, 37, -27) (-10, 3, 7) (-10, 14, -4) (-10, 39, -29) (10, -4, -6) (10, -36, 26) (10, -6, -4) (-23, 41, -18) (-23, -27, 50) (-23, 50, -27) (-23, 9, 14) (-23, 29, -6) (41, 9, -50) (41, -4, -37) (41, -37, -4) (-27, -4, 31) (-27, 31, -4) (50, -50, 0) (50, -50, 0) (9, 31, -40) (10, -4, -6) (10, -36, 26) (10, -6, -4) (-4, 0, 4) (-4, 4, 0) (-4, 31, -27) (6, 12, -18) (6, 0, -6) (6, 31, -37) (6, -6, 0) (12, 6, -18) (0, 4, -4) (0, 6, -6) (0, 29, -29) (0, 37, -37) (4, 32, -36) (4, 14, -18) (4, -4, 0) (31, 6, -37) (31, -27, -4) (6, -6, 0) (29, -36, 7) (29, 0, -29) (37, 3, -40) (37, -37, 0) (3, 26, -29) (-36, -6, 42) (14, -40, 26) In total: 1209 real user sys 0m0.085s 0m0.076s 0m0.008s Uwaga! Użytkownicy Windowsa mają do dyspozycji Measure-Commandw Powershellu. Korzystając z możliwości powłoki, nie musimy uruchamiać programu dla każdego rozmiaru ręcznie:
In [23]:!for N in 10 20 30 40 50; do time python3 3sum.py $N; done
(-31, 20, 11) (26, 9, -35) In total: 2 real 0m0.039s user 0m0.036s sys 0m0.000s (-3, 45, -42) (-28, -10, 38) (-28, -10, 38) (-28, -10, 38) (0, 14, -14) (41, 1, -42) (1, -10, 9) (1, -10, 9) (1, 9, -10) (-10, 6, 4) (-10, -28, 38) (-10, 6, 4) (-10, -28, 38) (9, -42, 33) (9, 5, -14) (6, -10, 4) (-28, -10, 38) (-42, 4, 38) In total: 18 real 0m0.035s user 0m0.028s sys 0m0.004s (41, -1, -40) (41, -1, -40) (41, -40, -1) (41, -45, 4) (41, -45, 4) (41, -49, 8) (41, -44, 3) (41, -1, -40) (-1, 39, -38) (-40, 37, 3) (-45, 35, 10) (-45, 10, 35) (-45, 37, 8) (-49, 10, 39) (-49, 12, 37) (35, 3, -38) (10, -18, 8) (10, -27, 17) (10, 3, -13) (35, 3, -38) (39, -1, -38) (39, 3, -42) (4, 11, -15) (12, 3, -15) (11, -15, 4) (37, -40, 3) (-27, 3, 24) In total: 27 real user 0m0.028s 0m0.024s
sys 0m0.000s (15, 14, -29) (15, 17, -32) (15, -36, 21) (15, -36, 21) (15, -14, -1) (14, 17, -31) (14, 10, -24) (14, -24, 10) (11, -38, 27) (11, 5, -16) (11, -36, 25) (11, -36, 25) (11, -32, 21) (11, -32, 21) (11, 20, -31) (-22, -5, 27) (-22, 17, 5) (-22, -24, 46) (-22, -14, 36) (-22, -5, 27) (-22, -5, 27) (45, -16, -29) (45, -14, -31) (-5, -38, 43) (-5, 41, -36) (-5, 10, -5) (-5, 10, -5) (-5, -24, 29) (-5, -16, 21) (-5, -16, 21) (-5, -5, 10) (-5, -31, 36) (-5, -5, 10) (-38, 17, 21) (-38, 17, 21) (-38, -5, 43) (-38, -5, 43) (17, -16, -1) (-48, 5, 43) (-48, 21, 27) (-48, 21, 27) (41, -36, -5) (41, -36, -5) (10, -36, 26) (10, 21, -31) (10, -5, -5) (10, -31, 21) (-24, -16, 40) (-24, -1, 25) (-24, -1, 25) (-24, 29, -5) (-24, 29, -5) (-33, -11, 44) (46, -32, -14) (5, -32, 27) (5, 26, -31) (-36, 26, 10) (-36, -14, 50) (-16, -11, 27) (-16, 21, -5)
(-16, 21, -5) (-16, -5, 21) (-16, -5, 21) (-32, -11, 43) (-14, -11, 25) (-14, -11, 25) (-14, -29, 43) (-11, -33, 44) (-11, -29, 40) (21, -31, 10) (-5, -31, 36) (-5, -5, 10) (-31, -5, 36) (-31, 21, 10) In total: 74 real 0m0.028s user 0m0.024s sys 0m0.000s (11, -18, 7) (11, -18, 7) (11, -4, -7) (11, -20, 9) (11, -20, 9) (11, -20, 9) (11, -20, 9) (11, -11, 0) (11, 7, -18) (11, 13, -24) (11, -18, 7) (-39, -4, 43) (-39, 1, 38) (-39, 48, -9) (-39, -11, 50) (-39, -9, 48) (-17, -4, 21) (-17, -20, 37) (-17, 10, 7) (-17, 10, 7) (-17, -20, 37) (-17, 10, 7) (-17, 10, 7) (-17, 31, -14) (-17, -14, 31) (-17, -24, 41) (-18, 40, -22) (-18, -20, 38) (-18, -20, 38) (-18, -27, 45) (-18, 9, 9) (-18, -13, 31) (-18, -13, 31) (-18, 27, -9) (-18, 42, -24) (-18, 42, -24) (-18, -24, 42) (-4, 40, -36) (-4, 40, -36) (-4, 48, -44) (-4, -27, 31) (-4, -27, 31)
(-4, -44, 48) (-4, 13, -9) (47, -20, -27) (47, -20, -27) (47, -36, -11) (47, -11, -36) (40, -20, -20) (40, -50, 10) (40, -50, 10) (40, 10, -50) (40, -27, -13) (40, 10, -50) (40, -22, -18) (-20, 10, 10) (-20, -27, 47) (-20, -27, 47) (-20, -11, 31) (-20, -11, 31) (-20, 7, 13) (-20, 13, 7) (-20, -22, 42) (-20, -22, 42) (-20, -22, 42) (-20, 27, -7) (-20, -18, 38) (-50, 49, 1) (-50, 43, 7) (-50, 43, 7) (-50, 9, 41) (-50, 37, 13) (-50, 9, 41) (-50, 0, 50) (49, 1, -50) (49, -36, -13) (49, -27, -22) (49, -13, -36) (10, 1, -11) (10, -20, 10) (1, 43, -44) (1, -11, 10) (1, 13, -14) (1, -22, 21) (43, -36, -7) (43, 7, -50) (43, -36, -7) (43, 7, -50) (-20, -27, 47) (-20, -27, 47) (-20, -11, 31) (-20, -11, 31) (-20, 7, 13) (-20, 13, 7) (-20, -22, 42) (-20, -22, 42) (-20, -22, 42) (-20, 27, -7) (-20, -18, 38) (-36, -11, 47) (-36, -11, 47) (-36, 9, 27) (-36, -14, 50)
(-36, 9, 27) (-36, -9, 45) (-27, -11, 38) (-27, -14, 41) (-27, 27, 0) (-27, -18, 45) (-11, 47, -36) (-11, -36, 47) (9, 13, -22) (9, 9, -18) (9, 27, -36) (9, -9, 0) (9, -50, 41) (-44, 7, 37) (-44, 37, 7) (-44, 31, 13) (-44, 13, 31) (7, -14, 7) (7, 0, -7) (37, -13, -24) (37, 13, -50) (-13, 31, -18) (-13, 13, 0) (-13, -14, 27) (-13, -18, 31) (31, -22, -9) (31, -7, -24) (13, -22, 9) (-22, -9, 31) (-14, -36, 50) (-14, 21, -7) (-14, 38, -24) (9, 27, -36) (9, -9, 0) (9, -50, 41) (27, -9, -18) (-36, -9, 45) (-18, 42, -24) (-18, 42, -24) (-18, -24, 42) (7, 0, -7) (0, -50, 50) (31, -7, -24) In total: 146 real user sys 0m0.033s 0m0.024s 0m0.008s Nie jest to zbyt przejrzyste, dlatego zmodyfikujemy nieco nasz program:
In [24]: %%writefile 3sum_2.py """3SUM problem solved with brute-force algorithm""" def counttriplets(tab): count = 0 triplets = [] for i in range(len(tab)): for j in range(i+1, len(tab)): for k in range(j+1, len(tab)): if (tab[i]+tab[j]+tab[k]==0): count = count + 1 triplets.append((tab[i],tab[j],tab[k])) return triplets if name == " main ": import sys import random n = int(sys.argv[1]) tab = [] for i in range(n): tab.append(random.randint(-50,50)) triplets = counttriplets(tab) print("\nsystem size: %d"%n) Overwriting 3sum_2.py
In [25]:!for N in 10 20 30 40 50; do time python3 3sum_2.py $N; done System size: 10 real user sys 0m0.031s 0m0.028s 0m0.000s System size: 20 real user sys 0m0.029s 0m0.024s 0m0.004s System size: 30 real user sys 0m0.027s 0m0.024s 0m0.000s System size: 40 real user sys 0m0.029s 0m0.024s 0m0.004s System size: 50 real user sys 0m0.029s 0m0.024s 0m0.004s Metoda 3 pomiar z poziomu programu Jeżeli wyników pomiaru czasu chcemy potem używać do analiz, najwygodniejszy może okazać się pomiar czasu wykonywania z poziomu analizowanego programu. Najprościej jest użyć do tego modułu time: In [26]: import time
In [27]: #sekundy... time.time() Out[27]: 1474631774.1989377 In [28]: #... które upłynęły od time.gmtime(0)[:6] Out[28]: (1970, 1, 1, 0, 0, 0) In [29]: #jak mierzyć? def procedure(): time.sleep(2.5) In [30]: # czas procesora, jaki został skonsumowany # przez proces ("process time") t0 = time.clock() procedure() t1 = time.clock() print("czas procesora:", t1-t0, "s") Czas procesora: 0.0019679999999999698 s In [31]: # rzeczywisty czas ("wall time") t0 = time.time() procedure() t1 = time.time() print("rzeczywisty czas:", t1-t0, "s") Rzeczywisty czas: 2.5009217262268066 s Warto wiedzieć, że nie wszystkie systemy (z Windows włącznie) mogą mierzyć czas procesora. Funkcja clockpokaże wówczas czas rzeczywisty. Wróćmy do naszego programu:
In [34]: %%writefile 3sum_3.py """3SUM problem solved with brute-force algorithm""" def counttriplets(tab): count = 0 triplets = [] for i in range(len(tab)): for j in range(i+1, len(tab)): for k in range(j+1, len(tab)): if (tab[i]+tab[j]+tab[k]==0): count = count + 1 triplets.append((tab[i],tab[j],tab[k])) return triplets if name == " main ": import sys import time import random n = int(sys.argv[1]) tab = [] for i in range(n): tab.append(random.randint(-50,50)) t = time.clock() #czas start triplets = counttriplets(tab) t = time.clock() - t #ile trwało? print("%d %f"%(n,t)) Overwriting 3sum_3.py In [35]:!python3 3sum_3.py 10 10 0.000057
In [39]:!for N in 10 20 50 100 200 500 1000; do python3 3sum_3.py $N; done 10 0.000059 20 0.000282 50 0.003865 100 0.025720 200 0.174831 500 2.695456 1000 21.228454 Zamiast wypisywać na ekran, zapiszmy te wyniki do pliku: In [40]:!for N in 10 20 50 100 200 500 1000 2000; do python3 3sum_3.py $N >> wyniki.dat; done In [32]:!cat wyniki.dat 10 0.000066 20 0.000292 50 0.003632 100 0.025550 200 0.173205 500 2.665352 1000 22.351571 2000 171.924855 Krok 3 wykres czasu wykonania w funkcji rozmiaru danych wejściowych In [34]: %matplotlib inline In [36]: import matplotlib.pyplot as plt import numpy as np x,y = np.loadtxt("wyniki.dat",unpack=true) #wczytujemy dane z pliku print(x) print(y) [ 10. 20. 50. 100. 200. 500. 1000. 2000.] [ 6.60000000e-05 2.92000000e-04 3.63200000e-03 2.55500000e-0 2 1.73205000e-01 2.66535200e+00 2.23515710e+01 1.71924855e+0 2]
In [39]: plt.plot(x,y,'ro',label="dane") plt.xlabel("rozmiar") plt.ylabel("czas wykonania") plt.legend(loc='upper left') plt.title("zagadnienie 3SUM metodą brute-force") plt.show() Wykres logarytmiczny powinien być bardziej czytelny:
In [41]: plt.loglog(x,y,'ro',label="dane") plt.xlabel("rozmiar") plt.ylabel("czas wykonania") plt.legend(loc='upper left') plt.title("zagadnienie 3SUM metodą brute-force") plt.show()
Krok 4 hipoteza Spróbujmy teraz przybliżyć dane przedstawione na powyższym wykresie jakąś "prostą" funkcją matematyczną. Ponieważ na wykresie logarytmicznym układają się w linię prostą, spodziewamy się zależności potęgowej: Rzeczywiście, jeśli zlogarytmujemy obustronnie powyższe równanie, otrzymamy czyli równanie prostej. y = ax b log y = b log x + log a Ponadto, algorytm użyty w programie 3sumwykorzystuje potrójne zagnieżdżenie. Dlatego przypuszczamy, że czas jego wykonania będzie proporcjonalny do, gdzie to rozmiar danych wejściowych. N 3 N In [44]: from scipy.optimize import curve_fit def func(x, a, b): return a*x**3 + b popt, pcov = curve_fit(func, x, y) print("a = %s, b = %s" % (popt[0], popt[1])) a = 2.1488626911e-08, b = 0.108065584827 Wynika stąd, że czas wykonania naszego algorytmu spełnia zależność: T(N) = 0.0000000214886 N 3 + 0.108065 Porównajmy dane i funkcję aproksymującą na wykresie:
In [46]: x2 = np.arange(1,2000) plt.plot(x,y,'ro',label="dane") plt.plot(x2, func(x2, *popt), label="hipoteza") plt.xlabel("rozmiar") plt.ylabel("czas wykonania") plt.legend(loc='upper left') plt.title("zagadnienie 3SUM metodą brute-force") plt.show() Krok 5 przewidywanie Interesuje nas, ile będzie wykonywał się program dla np. N = 1200:
In [47]: func(1200,*popt) Out[47]: 37.240412886967256 Krok 6 weryfikacja In [48]:!python3 3sum_3.py 1200 1200 36.325262 Dane empiryczne potwierdzają naszą hipotezę. Hipoteza podwojenia Istnieje sposób na szybkie oszacowanie współczynnika w prawie potęgowym Hipoteza podwojenia (ang. doubling hypothesis) związana jest z próbą odpowiedzi na pytanie: b T(N) = an b Jaki wpływ na czas pracy programu ma podwojenie rozmiaru danych wejściowych? Sposób postępowania: przygotowujemy tabelę, która zawiera 4 kolumny: rozmiar danych N czas wykonania T(N) stosunek czasów wykonania logarytm poprzedniej wielkości, tzn. T(2N)/T(N) log 2 (T(2N)/T(N)) rozmiary wybieramy tak, aby kolejny był dwa razy większy od poprzedniego uruchamiamy program dla tych rozmiarów i mierzymy czasy jego wykonania In [59]:!for N in 16 32 64 128 256 512 1024; do python3 3sum_3.py $N >> podwojenie.dat; done In [61]:!cat podwojenie.dat 16 0.000233 32 0.001232 64 0.007805 128 0.054047 256 0.389704 512 3.221152 1024 24.491979
In [62]: xtab = [] ytab = [] with open("podwojenie.dat") as f: for line in f: x, y = line.split() xtab.append(int(x)) ytab.append(float(y)) In [63]: xtab Out[63]: [16, 32, 64, 128, 256, 512, 1024] In [64]: ytab Out[64]: [0.000233, 0.001232, 0.007805, 0.054047, 0.389704, 3.221152, 24.491 979] In [65]: ratio = [None]*len(ytab) In [66]: ratio Out[66]: [None, None, None, None, None, None, None] In [67]: for i in range(1,len(ytab)): ratio[i] = ytab[i]/ytab[i-1] In [68]: ratio Out[68]: [None, 5.28755364806867, 6.3352272727272725, 6.924663677130044, 7.210464965678021, 8.265637509494386, 7.603484405579122]
In [72]: import math lratio = [None] In [73]: for val in ratio: if val: lratio.append(math.log(val,2)) In [74]: lratio Out[74]: [None, 2.4026003960406213, 2.66339637617037, 2.7917440028413196, 2.8500922944243596, 3.0471260955389763, 2.9266607057458383] In [76]: print("{} \t {} \t {} \t {}".format("n", "T", "Ratio", "Log")) for i in range(len(ytab)): print("{} \t {} \t {} \t {}".format(xtab[i], ytab[i], ratio[i], lratio[i])) N T Ratio Log 16 0.000233 None None 32 0.001232 5.28755364806867 2.4026003960406213 64 0.007805 6.3352272727272725 2.66339637617037 128 0.054047 6.924663677130044 2.7917440028413196 256 0.389704 7.210464965678021 2.8500922944243596 512 3.221152 8.265637509494386 3.0471260955389763 1024 24.491979 7.603484405579122 2.9266607057458383 Kontynuując te obliczenia zobaczylibyśmy, że wartość Wartość stałej czyli a lo g2(t(2n)/t(n)) 3 T(N) = an 3 zbiega do wartości, tzn.: możemy wyznaczyć rozwiązując to równanie dla wybranego rozmiaru danych, np.: 3.221152 = a 512 3 a = 0, 000000024