PHP をしばらく使用していますが、PHP に組み込まれているすべての関数が期待どおりに高速であるわけではないことに気付きました。キャッシュされた素数の配列を使用して数値が素数であるかどうかを調べる関数の、次の 2 つの実装を検討してください。
//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 );
}
in_array
これは、が線形検索 O(n) で実装されており、$prime_array
が大きくなるにつれて線形に速度が低下するためです。array_key_exists
関数はハッシュ検索 O(1) で実装されており、ハッシュ テーブルが極端に多くならない限り速度は低下しません (その場合、速度は O(n) のみになります)。
これまで私は試行錯誤しながらビッグオーを発見し、時にはソースコードを見るさて、質問です...
すべての組み込み PHP 関数の理論上の (または実際の) big O 時間のリストはありますか?
*少なくとも興味深いもの
array_merge
たとえば、可能な実装は PHP の未知のコアデータ構造 ( 、、、、(配列入力付き) など)に依存するため、リストさarray_merge_recursive
れている関数の大きなO を予測するのは非常に困難ですarray_reverse
。array_intersect
array_combine
str_replace
ベストアンサー1
これまで誰もこれをやったことがないようなので、どこかに参考として置いておくのがいいだろうと思いました。ベンチマークやコードスキミングで関数の特徴を調べましたarray_*
。より興味深い Big-O を一番上に置くようにしました。このリストは完全ではありません。
注: すべての Big-O は、ハッシュ検索が O(1) であると仮定して計算されていますが、実際には O(n) です。n の係数は非常に低いため、検索 Big-O の特性が効果を発揮し始める前に、十分に大きな配列を格納するための RAM オーバーヘッドが問題になります。たとえば、array_key_exists
N=1 での呼び出しと N=1,000,000 での呼び出しの違いは、約 50% の時間増加です。
興味深い点:
isset
/ は、およびarray_key_exists
よりもはるかに高速ですin_array
array_search
+
(union) は () よりも少し高速でarray_merge
、見た目もきれいです。ただし、動作が異なるので、その点に注意してください。shuffle
同じBig-O層にあるarray_rand
array_pop
/は再インデックスのペナルティにより/array_push
より高速ですarray_shift
array_unshift
検索:
array_key_exists
O(n) ですが、O(1) に非常に近いです。これは衝突時の線形ポーリングによるものですが、衝突の可能性は非常に小さいため、係数も非常に小さくなります。より現実的な big-O を得るには、ハッシュ検索を O(1) として扱うのがよいと思います。たとえば、N=1000 と N=100000 の違いは、速度低下が約 50% だけです。
isset( $array[$index] )
O(n) ですが、O(1) に非常に近いです。array_key_exists と同じ検索を使用します。これは言語構造であるため、キーがハードコードされている場合は検索をキャッシュし、同じキーが繰り返し使用される場合に速度が向上します。
in_array
O(n) - これは、値が見つかるまで配列全体を線形検索するためです。
array_search
O(n) - in_array と同じコア関数を使用しますが、値を返します。
キュー機能:
array_push
O(∑ var_i、すべてのiに対して)
array_pop
オー(1)
array_shift
O(n) - すべてのキーを再インデックスする必要がある
array_unshift
O(n + ∑ var_i, すべてのiに対して) - すべてのキーを再インデックスする必要がある
配列の交差、結合、減算:
array_intersect_key
交差が 100% の場合は O(Max(param_i_size)*∑param_i_count, すべての i について) を実行し、交差が 0% の場合は交差 O(∑param_i_size, すべての i について) を実行します。
array_intersect
交差が 100% の場合、O(n^2*∑param_i_count、すべての i について) を実行し、交差が 0% の場合、交差は O(n^2) になります。
array_intersect_assoc
交差が 100% の場合は O(Max(param_i_size)*∑param_i_count, すべての i について) を実行し、交差が 0% の場合は交差 O(∑param_i_size, すべての i について) を実行します。
array_diff
O(π param_i_size、すべてのiについて) - これはすべてのparam_sizesの積です
array_diff_key
O(∑ param_i_size, for i != 1) - これは、最初の配列を反復処理する必要がないためです。
array_merge
O( ∑ array_i, i != 1 ) - 最初の配列を反復処理する必要がない
+
(union) O(n)、ここでnは2番目の配列のサイズ(つまりarray_first + array_second)です。番号を再割り当てする必要がないため、array_mergeよりもオーバーヘッドが少なくなります。
array_replace
O( ∑ array_i、すべてのiに対して )
ランダム:
shuffle
の上)
array_rand
O(n) - 線形ポーリングが必要です。
明らかなビッグオー:
array_fill
の上)
array_fill_keys
の上)
range
の上)
array_splice
O(オフセット + 長さ)
array_slice
O(オフセット + 長さ)、または長さ = NULL の場合は O(n)
array_keys
の上)
array_values
の上)
array_reverse
の上)
array_pad
O(パッドサイズ)
array_flip
の上)
array_sum
の上)
array_product
の上)
array_reduce
の上)
array_filter
の上)
array_map
の上)
array_chunk
の上)
array_combine
の上)
感謝したいユーレカ関数の Big-O を簡単に見つけられるようにします。任意のデータに最適な関数を見つけることができる素晴らしい無料プログラムです。
編集:
PHP 配列検索が であることを疑う人のためにO(N)
、それをテストするためのベンチマークを作成しました (ほとんどO(1)
の現実的な値に対しては依然として効果的です)。
$tests = 1000000;
$max = 5000001;
for( $i = 1; $i <= $max; $i += 10000 ) {
//create lookup array
$array = array_fill( 0, $i, NULL );
//build test indexes
$test_indexes = array();
for( $j = 0; $j < $tests; $j++ ) {
$test_indexes[] = rand( 0, $i-1 );
}
//benchmark array lookups
$start = microtime( TRUE );
foreach( $test_indexes as $test_index ) {
$value = $array[ $test_index ];
unset( $value );
}
$stop = microtime( TRUE );
unset( $array, $test_indexes, $test_index );
printf( "%d,%1.15f\n", $i, $stop - $start ); //time per 1mil lookups
unset( $stop, $start );
}