Intereting Posts
Проверка и преобразование десятичных значений в CakePHP Проблема синтаксического анализа RSS с помощью скрипта PHP PHP-форму отправить функции для получения / установки значений в многомерных массивах динамически Разница между php 4 и php 5? Использование проверки подлинности Windows с помощью php? Может ли определение множества констант вызвать проблемы с производительностью или памятью? Загрузка файла Laravel – расширение php_fileinfo не включено API геокодирования Google Maps – смещение страны, причудливые результаты mysql_connect возвращает «Не удается подключиться к локальному серверу MySQL через сокет» при удаленном соединении с хостом? Загрузка пользовательских классов в CodeIgniter? Каков наилучший способ обработки загруженных текстовых файлов с различными кодировками? Нужно писать в начале файла с PHP mysql_result () ожидает, что параметр 1 будет ресурсом, данный объект 301 перенаправление с PHP и MySQL на 404

Список функций Big-O для PHP

Некоторое время после использования PHP я заметил, что не все PHP встроены в функции так быстро, как ожидалось. Рассмотрим ниже две возможные реализации функции, которая находит, если число является простым, используя кешированный массив простых чисел.

//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)).

До сих пор мне приходилось обнаруживать большие-O через пробную версию и ошибки, а иногда и смотреть на исходный код . Теперь на вопрос …

Был ли список теоретических (или практических) больших O раз для всех * встроенных в PHP функций?

* или, по крайней мере, интересные

Например, очень трудно предсказать, что такое большая функция функций, потому что возможная реализация зависит от неизвестных структур данных ядра 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% увеличения времени.

    Интересные моменты :

    1. isset / array_key_exists намного быстрее, чем array_search и array_search
    2. + (union) немного быстрее, чем array_merge (и выглядит лучше). Но это работает по-разному, поэтому имейте это в виду.
    3. shuffle находится на том же уровне Big-O, что и array_rand
    4. array_pop / array_push быстрее, чем array_shift / array_unshift из-за переиндексации штрафа

    Поиск :

    array_key_exists O (n), но действительно близкий к O (1) – это из-за линейного опроса в столкновениях, а потому, что вероятность столкновения очень мала, коэффициент также очень мал. Я нахожу, что вы обрабатываете поиск хэша как O (1), чтобы дать более реалистичный большой-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, для всех 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, для i! = 1) – это потому, что нам не нужно перебирать первый массив.

    array_merge O (Σ array_i, i! = 1) – не нужно перебирать первый массив

    + (объединение) O (n), где n – размер 2-го массива (т. е. array_first + array_second) – меньше накладных расходов, чем array_merge, поскольку он не имеет нумерации

    array_replace O (Σ array_i, для всех i)

    Случайный :

    shuffle O (n)

    array_rand O (n) – Требуется линейный опрос.

    Очевидный Большой-O :

    array_fill O (n)

    array_fill_keys O (n)

    range O (n)

    array_splice O (смещение + длина)

    array_slice O (смещение + длина) или O (n), если length = NULL

    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)

    Я хотел бы поблагодарить Eureqa за то, что вы легко нашли Big-O функций. Это потрясающая бесплатная программа, которая может найти наилучшую подходящую функцию для произвольных данных.

    РЕДАКТИРОВАТЬ:

    Для тех, кто сомневается в том, что поиск в массиве PHP – это O(N) , я написал тест, чтобы проверить это (они по-прежнему эффективно O(1) для большинства реалистичных значений).

    График поиска php-массива

     $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 ); } 

    Объяснение конкретного случая, которое вы конкретно описываете, это то, что ассоциативные массивы реализованы как хеш-таблицы, поэтому поиск по ключу (и, соответственно, array_key_exists ) равен O (1). Однако массивы не индексируются по значению, поэтому единственный способ в общем случае выяснить, существует ли значение в массиве, – это линейный поиск. Там нет ничего удивительного.

    Я не думаю, что есть специальная всесторонняя документация об алгоритмической сложности методов PHP. Однако, если это достаточно большой интерес, чтобы оправдать усилия, вы всегда можете просмотреть исходный код .

    Вы почти всегда хотите использовать isset вместо array_key_exists . Я не смотрю на внутренности, но я уверен, что array_key_exists – это O (N), потому что он выполняет итерацию по каждому ключу массива, а isset пытается получить доступ к элементу, используя тот же алгоритм хеширования, который используется, когда вы получаете доступ к индексу массива. Это должно быть O (1).

    Один из них: «получить» – это:

     $search_array = array('first' => null, 'second' => 4); // returns false isset($search_array['first']); // returns true array_key_exists('first', $search_array); 

    Мне было любопытно, поэтому я сравнил разницу:

     <?php $bigArray = range(1,100000); $iterations = 1000000; $start = microtime(true); while ($iterations--) { isset($bigArray[50000]); } echo 'is_set:', microtime(true) - $start, ' seconds', '<br>'; $iterations = 1000000; $start = microtime(true); while ($iterations--) { array_key_exists(50000, $bigArray); } echo 'array_key_exists:', microtime(true) - $start, ' seconds'; ?> 

    is_set: 0.132308959961 секунд
    array_key_exists: 2.33202195168 секунд

    Конечно, это не показывает временную сложность, но показывает, как 2 функции сравниваются друг с другом.

    Чтобы проверить временную сложность, сравните время, затрачиваемое на выполнение одной из этих функций на первом и последнем ключах.

    Если бы люди столкнулись с трудностями на практике с ключевыми столкновениями, они реализовали бы контейнеры со вторичным поиском хэшей или сбалансированным деревом. Сбалансированное дерево дало бы поведение O (log n) худшего случая и O (1) avg. case (сам хэш). Накладные расходы не стоят в большинстве практических приложений с памятью, но, возможно, есть базы данных, которые реализуют эту форму смешанной стратегии в качестве случая по умолчанию.