[解決済み] Big-O for PHP関数一覧
質問
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(n)で済む)、速度が低下することはないでしょう。
これまでは試行錯誤でbig-Oを発見する必要があり、時には ソースコードを見る . では、質問です...
PHP の組み込み関数すべて* の理論的な (あるいは実際的な) 大 O 回数の一覧はありますか?
*または少なくとも興味深いもの
例えば、リストアップされた関数のうち、大きなOを予測するのは非常に困難です。なぜなら、実装の可能性が、PHPの未知のコアデータ構造に依存するからです。
array_merge
,
array_merge_recursive
,
array_reverse
,
array_intersect
,
array_combine
,
str_replace
(配列入力の場合)など。
どのように解決するのですか?
誰もやったことがないようなので、どこかで参考にしたほうがいいと思いました。私は、ベンチマークやコードスキミングによって
array_*
という関数があります。より興味深いBig-Oをトップに近いところに置くようにしました。このリストは完全ではありません。
注:すべてのBig-Oは、ハッシュのルックアップがO(1)であると仮定して計算されているが、実際にはO(n)である。nの係数は非常に小さく、ルックアップBig-Oの特性が効果を発揮する前に、十分に大きな配列を格納するためのラムオーバーヘッドが痛手となります。たとえば
array_key_exists
N=1とN=1,000,000では、50%程度の時間増加です。
興味深い点 :
-
isset
/array_key_exists
よりもはるかに高速です。in_array
とarray_search
-
+
(ユニオン) の方が少し速いです。array_merge
(そして見た目もきれい)。しかし、動作は異なるので、その点は注意してください。 -
shuffle
と同じBig-Oの階層にあります。array_rand
-
array_pop
/array_push
よりも高速です。array_shift
/array_unshift
再インデックスペナルティにより
ルックアップ :
array_key_exists
O(n)ですが、本当にO(1)に近いです。これは衝突の線形ポーリングのためですが、衝突の確率が非常に小さいため、係数も非常に小さくなります。ハッシュのルックアップをO(1)として扱うと、より現実的なBig-Oが得られると思います。例えば、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
O(1)
array_shift
O(n) - すべてのキーのインデックスを再作成する必要があります。
array_unshift
O(n + ∑ var_i, for all i) - すべてのキーのインデックスを再作成する必要があります。
配列の交差、結合、減算 :
array_intersect_key
交差点が100%の場合はO(Max(param_i_size)*∑param_i_count, for all i), 交差点が0%の場合は O(∑param_i_size, for all i) を実行する。
array_intersect
交差点が100%の場合はO(n^2*∑param_i_count, for all i)、交差点が0%の場合はO(n^2)
array_intersect_assoc
交差点が100%の場合はO(Max(param_i_size)*∑param_i_count, for all i), 交差点が0%の場合はO(∑param_i_size, for all i) を実行します。
array_diff
O(π param_i_size, for all i) - これはすべてのparam_sizeの積です.
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, for all i )
ランダム :
shuffle
O(n)
array_rand
O(n) - リニアポールが必要です。
明らかなBig-O :
array_fill
O(n)
array_fill_keys
O(n)
range
O(n)
array_splice
O(オフセット+長さ)
array_slice
O(offset + length) または length = NULL の場合は O(n)
array_keys
O(n)
array_values
O(n)
array_reverse
O(n)
array_pad
O(pad_size)
array_flip
O(n)
array_sum
O(n)
array_product
O(n)
array_reduce
O(n)
array_filter
O(n)
array_map
O(n)
array_chunk
O(n)
array_combine
O(n)
感謝する ユーレカ 関数のBIG-Oを簡単に探せるようにしたこと。それは、驚くべき フリー は、任意のデータに対して最適な関数を求めることができるプログラムです。
EDIT
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 );
}
関連
-
[解決済み] SQLSTATE[HY093]: 無効なパラメータ番号: パラメータが定義されていません
-
[解決済み】count()パラメータは配列かlaravelのcountableを実装したオブジェクトでなければならない
-
[解決済み] PHP と mod_fcgid: handle_request_ipc 関数で ap_pass_brigade が失敗する。
-
[解決済み] SSLエラー SSL3_GET_SERVER_CERTIFICATE:証明書の検証に失敗しました。
-
[解決済み] PHPで配列から要素を削除する
-
[解決済み] PHPでSQLインジェクションを防ぐにはどうしたらいいですか?
-
[解決済み] PHPのstartWith()関数とendsWith()関数
-
[解決済み] ビッグ・オー、どうやって計算・概算するんだ?
-
[解決済み】PHPの'foreach'は実際どのように動作するのですか?
-
[解決済み] リファレンス - このシンボルはPHPで何を意味するのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] PHP & MySQL: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given [重複] PHP & MySQL: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given.
-
[解決済み] コマンドの同期がとれていない。
-
[解決済み】mysqli_result クラスのオブジェクトを文字列に変換できない
-
[解決済み】変な電話番号を生成するフェイカー?
-
[解決済み] SQLSTATE[HY093]: 無効なパラメータ番号: バインドされた変数の数が102行目のトークンの数と一致しない [終了]
-
[解決済み】警告:mysql_fetch_array()はパラメータ1がリソースであることを期待、ブール値は[重複]で与えられる]
-
[解決済み】PHPのクラスが見つからないが、インクルードされている
-
thinkphp5 timestamp 非整形の数値に遭遇した。
-
[解決済み] Uncaught Error: 未定義の関数 mysql_escape_string() の呼び出し。
-
[解決済み] PHP 未定義関数への呼び出し