Setelah menggunakan PHP untuk sementara waktu sekarang, saya perhatikan bahwa tidak semua fungsi PHP built-in secepat yang diharapkan. Pertimbangkan dua kemungkinan implementasi dari fungsi ini yang menemukan jika suatu bilangan prima menggunakan array bilangan prima yang di-cache
//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 );
}
Ini karena in_array
diimplementasikan dengan pencarian linear O (n) yang secara linear akan melambat seiring dengan $prime_array
pertumbuhan. Di mana array_key_exists
fungsi diimplementasikan dengan pencarian hash O (1) yang tidak akan melambat kecuali tabel hash menjadi sangat padat (dalam hal ini hanya O (n)).
Sejauh ini saya harus menemukan big-O melalui trial and error, dan sesekali melihat kode sumber . Sekarang untuk pertanyaan ...
Apakah ada daftar waktu O besar teoritis (atau praktis) untuk semua * fungsi PHP bawaan?
* atau setidaknya yang menarik
Sebagai contoh, saya merasa sangat sulit untuk memprediksi O besar fungsi yang terdaftar karena kemungkinan pelaksanaan tergantung pada struktur data inti yang tidak diketahui dari PHP: array_merge
, array_merge_recursive
, array_reverse
, array_intersect
, array_combine
, str_replace
(dengan input array), dll
true
dan kemudian menguji keberadaan menggunakanisset($large_prime_array[$number])
. Jika saya ingat dengan benar, urutannya ratusan kali lebih cepat daripadain_array
fungsinya.array_key_exists
, saya membandingkanin_array
.in_array
iterates setiap item dalam array dan membandingkan nilai dengan jarum yang Anda berikan. Jika Anda membalikkan nilai ke kunci (dan hanya mengganti masing-masing nilai dengan nilai dummy sepertitrue
, menggunakanisset
banyak kali lebih cepat. Ini karena kunci array diindeks oleh PHP (seperti hashtable). Akibatnya, mencari sebuah array dengan cara ini dapat meningkatkan kecepatan secara signifikanJawaban:
Karena sepertinya tidak ada yang melakukan ini sebelum saya pikir itu ide yang baik untuk memilikinya untuk referensi di suatu tempat. Saya sudah pergi dan baik melalui benchmark atau skimming kode untuk mengkarakterisasi
array_*
fungsi. Saya sudah mencoba menempatkan Big-O yang lebih menarik di dekat bagian atas. Daftar ini tidak lengkap.Catatan: Semua Big-O yang dihitung dengan asumsi pencarian hash adalah O (1) meskipun itu benar-benar O (n). Koefisien dari n sangat rendah, overhead ram menyimpan array yang cukup besar akan menyakiti Anda sebelum karakteristik pencarian Big-O akan mulai berlaku. Misalnya perbedaan antara panggilan ke
array_key_exists
di N = 1 dan N = 1.000.000 adalah ~ 50% peningkatan waktu.Poin Menarik :
isset
Sayaarray_key_exists
jauh lebih cepat daripadain_array
danarray_search
+
(Serikat pekerja) sedikit lebih cepat daripadaarray_merge
(dan terlihat lebih bagus). Tapi itu bekerja secara berbeda jadi ingatlah itu.shuffle
berada pada tingkat Big-O yang sama denganarray_rand
array_pop
/array_push
lebih cepat dariarray_shift
/array_unshift
karena penalti indeks ulangPencarian :
array_key_exists
O (n) tetapi sangat dekat dengan O (1) - ini karena jajak pendapat linear dalam tabrakan, tetapi karena kemungkinan tabrakan sangat kecil, koefisiennya juga sangat kecil. Saya menemukan Anda memperlakukan pencarian hash sebagai O (1) untuk memberikan O-besar yang lebih realistis. Misalnya perbedaan antara N = 1000 dan N = 100000 hanya sekitar 50% melambat.isset( $array[$index] )
O (n) tetapi sangat dekat dengan O (1) - ia menggunakan pencarian yang sama seperti array_key_exists. Karena konstruk bahasanya, akan men-cache pencarian jika kunci dikodekan, sehingga mempercepat dalam kasus di mana kunci yang sama digunakan berulang kali.in_array
O (n) - ini karena ia melakukan pencarian linear melalui array sampai menemukan nilainya.array_search
O (n) - ia menggunakan fungsi inti yang sama dengan in_array tetapi mengembalikan nilai.Fungsi antrian :
array_push
O (∑ var_i, untuk semua i)array_pop
O (1)array_shift
O (n) - itu harus mengindeks ulang semua kunciarray_unshift
O (n + ∑ var_i, untuk semua i) - ia harus mengindeks ulang semua kunciArray Intersection, Union, Subtraction :
array_intersect_key
jika persimpangan 100% lakukan O (Maks (param_i_size) * param_i_count, untuk semua i), jika persimpangan 0% berpotongan O (∑param_i_size, untuk semua i)array_intersect
jika persimpangan 100% lakukan O (n ^ 2 * param_i_count, untuk semua i), jika persimpangan 0% berpotongan O (n ^ 2)array_intersect_assoc
jika persimpangan 100% lakukan O (Maks (param_i_size) * param_i_count, untuk semua i), jika persimpangan 0% berpotongan O (∑param_i_size, untuk semua i)array_diff
O (π param_i_size, for all i) - Itu adalah produk dari semua param_sizesarray_diff_key
O (∑ param_i_size, untuk i! = 1) - ini karena kita tidak perlu mengulangi array pertama.array_merge
O (∑ array_i, i! = 1) - tidak perlu mengulangi array pertama+
(union) O (n), di mana n adalah ukuran array ke-2 (yaitu array_first + array_second) - lebih sedikit overhead daripada array_merge karena tidak harus dinomori ulangarray_replace
O (∑ array_i, untuk semua i)Acak :
shuffle
Di)array_rand
O (n) - Membutuhkan jajak pendapat linier.Jelas Big-O :
array_fill
Di)array_fill_keys
Di)range
Di)array_splice
O (offset + panjang)array_slice
O (offset + panjang) atau O (n) jika panjang = NULLarray_keys
Di)array_values
Di)array_reverse
Di)array_pad
O (pad_size)array_flip
Di)array_sum
Di)array_product
Di)array_reduce
Di)array_filter
Di)array_map
Di)array_chunk
Di)array_combine
Di)Saya ingin berterima kasih kepada Eureqa karena membuatnya mudah menemukan Big-O dari fungsinya. Ini adalah program gratis luar biasa yang dapat menemukan fungsi pemasangan terbaik untuk data sewenang-wenang.
EDIT:
Bagi mereka yang meragukan bahwa pencarian array PHP
O(N)
, saya telah menulis benchmark untuk menguji itu (mereka masih efektifO(1)
untuk nilai-nilai paling realistis).sumber
Penjelasan untuk kasus yang Anda gambarkan secara spesifik adalah bahwa array asosiatif diimplementasikan sebagai tabel hash - jadi pencarian dengan kunci (dan juga,
array_key_exists
) adalah O (1). Namun, array tidak diindeks oleh nilai, jadi satu-satunya cara dalam kasus umum untuk menemukan apakah suatu nilai ada dalam array adalah pencarian linear. Tidak ada kejutan di sana.Saya tidak berpikir ada dokumentasi komprehensif khusus tentang kompleksitas algoritmik metode PHP. Namun, jika ini masalah yang cukup besar untuk menjamin upaya tersebut, Anda selalu dapat melihat kode sumbernya .
sumber
Anda hampir selalu ingin menggunakan
isset
bukanarray_key_exists
. Saya tidak melihat internal, tapi saya cukup yakin ituarray_key_exists
adalah O (N) karena iterates atas masing-masing dan setiap kunci array, sementaraisset
mencoba mengakses elemen menggunakan algoritma hash yang sama yang digunakan ketika Anda mengakses indeks array. Itu harus O (1).Satu "gotcha" yang harus diperhatikan adalah ini:
Saya ingin tahu, jadi saya membandingkan perbedaannya:
is_set:
0,132308959961 detikarray_key_exists:
2,33202195168 detikTentu saja, ini tidak menunjukkan kompleksitas waktu, tetapi ini menunjukkan bagaimana kedua fungsi tersebut saling membandingkan.
Untuk menguji kompleksitas waktu, bandingkan jumlah waktu yang diperlukan untuk menjalankan salah satu fungsi ini pada tombol pertama dan terakhir.
sumber
$arrray[] = $append
vs.array_push($array, $append)
Kedua, array_key_exists juga membedakan antara nilai yang tidak disetel dan null. Untuk$a = array('fred' => null);
array_key_exists('fred', $a)
akan mengembalikan benar sementaraisset($['fred'])
akan mengembalikan salah. Langkah ekstra ini tidak sepele dan akan sangat meningkatkan waktu eksekusi.Jika orang-orang mengalami kesulitan dalam praktek dengan tabrakan kunci, mereka akan menerapkan kontainer dengan pencarian hash sekunder atau pohon seimbang. Pohon seimbang akan memberikan O (log n) perilaku kasus terburuk dan O (1) rata-rata. case (hash itu sendiri). Overhead tidak sepadan dengan yang paling praktis dalam aplikasi memori, tetapi mungkin ada database yang menerapkan bentuk strategi campuran ini sebagai kasus default mereka.
sumber