Po dłuższym użyciu PHP zauważyłem, że nie wszystkie wbudowane funkcje PHP działają tak szybko, jak się spodziewano. Rozważ te dwie możliwe implementacje funkcji, która sprawdza, czy liczba jest liczbą pierwszą, używając buforowanej tablicy liczb pierwszych.
//very slow for large $prime_array
$prime_array = array( 2, 3, 5, 7, 11, 13, .... 104729, ... );
$result_array = array();
foreach( $prime_array => $number ) {
$result_array[$number] = in_array( $number, $large_prime_array );
}
//speed is much less dependent on size of $prime_array, and runs much faster.
$prime_array => array( 2 => NULL, 3 => NULL, 5 => NULL, 7 => NULL,
11 => NULL, 13 => NULL, .... 104729 => NULL, ... );
foreach( $prime_array => $number ) {
$result_array[$number] = array_key_exists( $number, $large_prime_array );
}
Wynika to z faktu, że in_array
jest realizowane za pomocą wyszukiwania liniowego O (n), które będzie liniowo zwalniać w miarę $prime_array
wzrostu. Gdy array_key_exists
funkcja jest zaimplementowana z wyszukiwaniem skrótów O (1), który nie zwolni, chyba że tablica skrótów zostanie bardzo zapełniona (w takim przypadku jest to tylko O (n)).
Do tej pory musiałem odkrywać duże O poprzez próbę i błąd, a czasami od czasu do czasu patrzę na kod źródłowy . Teraz pytanie…
Czy istnieje lista teoretycznych (lub praktycznych) dużych czasów O dla wszystkich * wbudowanych funkcji PHP?
* lub przynajmniej te interesujące
Na przykład, bardzo trudno przewidzieć Big O funkcji wymienionych ponieważ ewentualna realizacja zależy od struktury danych brak podstawowych PHP: array_merge
, array_merge_recursive
, array_reverse
, array_intersect
, array_combine
, str_replace
(z wejściami tablicy), etc.
true
a następnie przetestowanie obecności za pomocąisset($large_prime_array[$number])
. Jeśli dobrze pamiętam, jest to setki razy szybsze niżin_array
funkcja.array_key_exists
, porównujęin_array
.in_array
iteruje każdy element w tablicy i porównuje wartość z igłą, którą mu przekazujesz. Jeśli przerzucisz wartości na klucz (i po prostu zastąpisz każdą z wartościami fikcyjnymi, np.true
Użycieisset
jest wiele razy szybsze. Jest tak, ponieważ klucze tablicy są indeksowane przez PHP (jak tablica haszująca). W rezultacie wyszukiwanie tablica w ten sposób może znacząco poprawić prędkośćOdpowiedzi:
Ponieważ wydaje się, że nikt tego nie zrobił, zanim pomyślałem, że dobrze byłoby mieć go gdzieś w celach informacyjnych. Poszedłem i albo poprzez test porównawczy lub przeglądanie kodu, aby scharakteryzować
array_*
funkcje. Próbowałem umieścić bardziej interesujący Big-O u góry. Ta lista nie jest kompletna.Uwaga: Wszystkie wartości Big-O zostały obliczone przy założeniu, że wyszukiwanie skrótem to O (1), nawet jeśli tak naprawdę to O (n). Współczynnik n jest tak niski, że obciążenie pamięci RAM związane z przechowywaniem wystarczająco dużej tablicy zraniłoby cię, zanim charakterystyka wyszukiwania Big-O zacznie działać. Na przykład różnica między połączeniem na numer
array_key_exists
N = 1 a N = 1 000 000 stanowi wzrost o ~ 50%.Ciekawe punkty :
isset
/array_key_exists
jest znacznie szybszy niżin_array
iarray_search
+
(union) jest nieco szybszy niżarray_merge
(i wygląda ładniej). Ale działa inaczej, więc miej to na uwadze.shuffle
jest na tym samym poziomie co Big-Oarray_rand
array_pop
/array_push
jest szybszy niżarray_shift
/ zarray_unshift
powodu kary ponownego indeksowaniaWyszukiwania :
array_key_exists
O (n), ale naprawdę bliskie O (1) - wynika to z liniowego odpytywania w zderzeniach, ale ponieważ prawdopodobieństwo kolizji jest bardzo małe, współczynnik jest również bardzo mały. Uważam, że traktujesz skróty jako O (1), aby uzyskać bardziej realistyczne duże-O. Na przykład różnica między N = 1000 a N = 100000 jest tylko około 50% spowolnieniem.isset( $array[$index] )
O (n), ale bardzo zbliżone do O (1) - używa tego samego wyszukiwania co tablica_klucz_istnieje. Ponieważ jest to konstrukcja językowa, buforuje wyszukiwanie, jeśli klucz jest zakodowany na stałe, co powoduje przyspieszenie w przypadkach, gdy ten sam klucz jest używany wielokrotnie.in_array
O (n) - dzieje się tak, ponieważ przeszukuje liniowo tablicę, dopóki nie znajdzie wartości.array_search
O (n) - wykorzystuje tę samą funkcję podstawową, co tablica_w, ale zwraca wartość.Funkcje kolejki :
array_push
O (∑ var_i, dla wszystkich i)array_pop
O (1)array_shift
O (n) - musi ponownie indeksować wszystkie kluczearray_unshift
O (n + ∑ var_i, dla wszystkich i) - musi ponownie indeksować wszystkie kluczePrzecięcie tablicy, łączenie, odejmowanie :
array_intersect_key
jeśli przecięcie 100% do O (Max (param_i_size) * ∑param_i_count, dla wszystkich i), jeśli przecięcie 0% przecina O (∑param_i_size, dla wszystkich i)array_intersect
jeśli przecięcie 100% do O (n ^ 2 * ∑param_i_count, dla wszystkich i), jeśli przecięcie 0% przecina O (n ^ 2)array_intersect_assoc
jeśli przecięcie 100% do O (Max (param_i_size) * ∑param_i_count, dla wszystkich i), jeśli przecięcie 0% przecina O (∑param_i_size, dla wszystkich i)array_diff
O (π param_i_size, for all i) - To iloczyn wszystkich param_sizesarray_diff_key
O (∑ param_i_size, dla i! = 1) - dzieje się tak, ponieważ nie musimy iterować po pierwszej tablicy.array_merge
O (∑ array_i, i! = 1) - nie trzeba iterować po pierwszej tablicy+
(union) O (n), gdzie n jest rozmiarem drugiej tablicy (tj. array_first + array_second) - mniej narzutu niż array_merge, ponieważ nie musi on przenumerowywaćarray_replace
O (∑ array_i, dla wszystkich i)Losowo :
shuffle
Na)array_rand
O (n) - Wymaga sondowania liniowego.Oczywiste Big-O :
array_fill
Na)array_fill_keys
Na)range
Na)array_splice
O (przesunięcie + długość)array_slice
O (przesunięcie + długość) lub O (n), jeśli długość = NULLarray_keys
Na)array_values
Na)array_reverse
Na)array_pad
O (rozmiar_padu)array_flip
Na)array_sum
Na)array_product
Na)array_reduce
Na)array_filter
Na)array_map
Na)array_chunk
Na)array_combine
Na)Chciałbym podziękować Eureqa za ułatwienie znalezienia Big-O funkcji. To niesamowity darmowy program, który może znaleźć najlepszą funkcję dopasowania do dowolnych danych.
EDYTOWAĆ:
Dla tych, którzy wątpią w wyszukiwanie tablic PHP
O(N)
, napisałem test porównawczy, aby to sprawdzić (wciąż są skuteczneO(1)
dla najbardziej realistycznych wartości).źródło
Wyjaśnienie opisanego przypadku jest takie, że tablice asocjacyjne są implementowane jako tabele skrótów - więc wyszukiwanie według klucza (i odpowiednio
array_key_exists
) to O (1). Jednak tablice nie są indeksowane według wartości, więc jedynym sposobem w ogólnym przypadku, aby dowiedzieć się, czy wartość istnieje w tablicy, jest wyszukiwanie liniowe. Nie ma w tym niespodzianki.Nie wydaje mi się, żeby istniała konkretna kompleksowa dokumentacja złożoności algorytmicznej metod PHP. Jeśli jednak jest to wystarczająco duży problem, aby uzasadnić wysiłek, zawsze możesz przejrzeć kod źródłowy .
źródło
Prawie zawsze chcesz używać
isset
zamiastarray_key_exists
. Nie patrzę na elementy wewnętrzne, ale jestem prawie pewien, żearray_key_exists
jest to O (N), ponieważ iteruje każdy klucz w tablicy, podczas gdyisset
próbuje uzyskać dostęp do elementu przy użyciu tego samego algorytmu skrótu, który jest używany podczas uzyskiwania dostępu indeks tablicy. To powinno być O (1).Jedną z rzeczy, na które trzeba uważać, jest:
Byłem ciekawy, więc porównałem różnicę:
is_set:
0,133308959961 sekundarray_key_exists:
2,33202195168 sekundOczywiście nie pokazuje to złożoności czasu, ale pokazuje porównanie dwóch funkcji między sobą.
Aby sprawdzić złożoność czasu, porównaj czas potrzebny do uruchomienia jednej z tych funkcji na pierwszym i ostatnim kluczu.
źródło
$arrray[] = $append
vs.array_push($array, $append)
Po drugie, array_key_exists rozróżnia także wartości nie ustawione i zerowe. Gdyż$a = array('fred' => null);
array_key_exists('fred', $a)
zwróci prawdę, aisset($['fred'])
zwróci fałsz. Ten dodatkowy krok nie jest trywialny i znacznie wydłuży czas wykonania.Gdyby ludzie mieli w praktyce kłopoty z kluczowymi kolizjami, wdrażaliby kontenery z wtórnym wyszukiwaniem skrótu lub zrównoważonym drzewem. Zrównoważone drzewo dałoby O (log n) najgorsze zachowanie i O (1) śr. case (sam skrót). Koszty ogólne nie są tego warte w najbardziej praktycznych aplikacjach pamięciowych, ale być może istnieją bazy danych, które domyślnie wdrażają tę formę strategii mieszanej.
źródło