Zrobiłem pracę domową i przypadkowo odkryłem dziwną niespójność w szybkości algorytmu. Oto 2 wersje kodu o tej samej funkcji bur z 1 różnicą: w pierwszej wersji używam 3-krotnego generatora tablicowego do filtrowania pewnej tablicy, aw drugiej wersji używam 1 dla pętli z 3 instrukcjami if, aby wykonać tę samą pracę filtru.Porównywanie zamiany list i pętli jawnych (3 generatory tablicowe szybsze niż 1 dla pętli)
Więc tutaj jest kod 1st wersji:
def kth_order_statistic(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = [x for x in array if x < pivot]
m = [x for x in array if x == pivot]
r = [x for x in array if x > pivot]
if k <= len(l):
return kth_order_statistic(l, k)
elif k > len(l) + len(m):
return kth_order_statistic(r, k - len(l) - len(m))
else:
return m[0]
A oto kod 2nd wersji:
def kth_order_statistic2(array, k):
pivot = (array[0] + array[len(array) - 1]) // 2
l = []
m = []
r = []
for x in array:
if x < pivot:
l.append(x)
elif x > pivot:
r.append(x)
else:
m.append(x)
if k <= len(l):
return kth_order_statistic2(l, k)
elif k > len(l) + len(m):
return kth_order_statistic2(r, k - len(l) - len(m))
else:
return m[0]
wyjście ipython dla 1 wersji:
In [4]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: order_statisctic(A, k)
...:
10 loops, best of 3: 120 ms per loop
a dla 2 wersja:
In [5]: %%timeit
...: A = range(100000)
...: shuffle(A)
...: k = randint(1, len(A)-1)
...: kth_order_statistic2(A, k)
...:
10 loops, best of 3: 169 ms per loop
Dlaczego pierwsza wersja jest szybsza od drugiej? Zrobiłem także trzecią wersję, która używa funkcji filter() zamiast generatora tablicowego i była wolniejsza niż druga wersja (ma 218 ms na pętlę).
Uporządkowanie listy często jest szybsze niż odpowiednik dla pętli. Rozszerzanie list (twoich funkcji dołączania) może być również droższe niż wypełnianie listy znanego rozmiaru. –
radykalnie zwiększysz swoją listę rozmiarów ... Myślę, że przekonasz się, że luka jest mniej więcej stała ... handlujesz przestrzenią na czas, ale są one * mniej więcej * ekwiwalentne (trochę narzutów generatorów/iteratory vs lista) –
@SohierDane To było moje myślenie. Zrozumienie listy jest jednoznaczne co do operacji, która tworzy listę, dlatego można na niej przeprowadzić optymalizację. Budując element list po elemencie, Python nie może przyjąć żadnych założeń dotyczących przyszłych obliczeń lub użycia pamięci. – sdsmith