Después de usar PHP por un tiempo, noté que no todas las funciones integradas de PHP son tan rápidas como se esperaba. Considere estas dos posibles implementaciones de una función que encuentra si un número es primo usando una matriz de primos en caché.
//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 );
}
Esto se debe a que in_array
se implementa con una búsqueda lineal O (n) que disminuirá linealmente a medida que $prime_array
crece. Donde la array_key_exists
función se implementa con una búsqueda hash O (1) que no se ralentizará a menos que la tabla hash se llene extremadamente (en cuyo caso es solo O (n)).
Hasta ahora he tenido que descubrir las grandes O a través de prueba y error, y ocasionalmente mirando el código fuente . Ahora para la pregunta ...
¿Existe una lista de los grandes tiempos teóricos (o prácticos) de O para todas * las funciones PHP integradas?
* o al menos los interesantes
Por ejemplo, se me hace muy difícil predecir el gran O de las funciones enumeradas debido a la posible aplicación depende de las estructuras de datos básicos desconocida de PHP: array_merge
, array_merge_recursive
, array_reverse
, array_intersect
, array_combine
, str_replace
(con las entradas de la matriz), etc.
true
y luego probar la presencia usandoisset($large_prime_array[$number])
. Si no recuerdo mal, está en el orden de ser cientos de veces más rápido que lain_array
función.array_key_exists
, me estoy comparandoin_array
.in_array
itera cada elemento de la matriz y compara el valor con la aguja que le pasa. Si cambia los valores a la clave (y simplemente reemplaza cada uno de los valores con un valor ficticio comotrue
, usarisset
es muchas veces más rápido. Esto se debe a que las claves de una matriz están indexadas por PHP (como una tabla hash). En consecuencia, la búsqueda una matriz de esta manera puede tener una mejora significativa en la velocidad.Respuestas:
Como no parece que alguien haya hecho esto antes, pensé que sería una buena idea tenerlo como referencia en alguna parte. Sin embargo, he ido a través de puntos de referencia o de descremado de código para caracterizar las
array_*
funciones. He tratado de poner el Big-O más interesante cerca de la parte superior. Esta lista no esta completa.Nota: Todo el Big-O donde se calculó suponiendo una búsqueda hash es O (1) a pesar de que realmente es O (n). El coeficiente de la n es tan bajo que la sobrecarga del ariete de almacenar una matriz lo suficientemente grande le haría daño antes de que las características de la búsqueda Big-O comenzaran a tener efecto. Por ejemplo, la diferencia entre una llamada a
array_key_exists
N = 1 y N = 1,000,000 es ~ 50% de aumento de tiempo.Puntos interesantes :
isset
/ /array_key_exists
es mucho más rápido quein_array
yarray_search
+
(union) es un poco más rápido quearray_merge
(y se ve mejor). Pero funciona de manera diferente, así que tenlo en cuenta.shuffle
está en el mismo nivel Big-O quearray_rand
array_pop
/array_push
es más rápido quearray_shift
/array_unshift
debido a la penalización de reindexaciónBúsquedas :
array_key_exists
O (n) pero muy cerca de O (1): esto se debe al sondeo lineal en colisiones, pero debido a que la posibilidad de colisiones es muy pequeña, el coeficiente también es muy pequeño. Me parece que tratas las búsquedas de hash como O (1) para dar un Big-O más realista. Por ejemplo, la diferencia entre N = 1000 y N = 100000 es solo un 50% de desaceleración.isset( $array[$index] )
O (n) pero muy cerca de O (1): utiliza la misma búsqueda que array_key_exists. Dado que es una construcción de lenguaje, almacenará en caché la búsqueda si la clave está codificada, lo que dará como resultado una aceleración en los casos en que la misma clave se use repetidamente.in_array
O (n): esto se debe a que realiza una búsqueda lineal a través de la matriz hasta que encuentra el valor.array_search
O (n): utiliza la misma función central que in_array pero devuelve valor.Funciones de cola :
array_push
O (∑ var_i, para todo i)array_pop
O (1)array_shift
O (n): debe reindexar todas las clavesarray_unshift
O (n + ∑ var_i, para todo i) - tiene que reindexar todas las clavesIntersección de matriz, unión, resta :
array_intersect_key
si la intersección 100% hace O (Max (param_i_size) * ∑param_i_count, para todo i), si la intersección 0% intersecta O (ramparam_i_size, para todo i)array_intersect
si la intersección 100% hace O (n ^ 2 * ∑param_i_count, para todo i), si la intersección 0% intersecta O (n ^ 2)array_intersect_assoc
si la intersección 100% hace O (Max (param_i_size) * ∑param_i_count, para todo i), si la intersección 0% intersecta O (ramparam_i_size, para todo i)array_diff
O (π param_i_size, para todo i) - Eso es producto de todos los param_sizesarray_diff_key
O (∑ param_i_size, para i! = 1): esto se debe a que no necesitamos iterar sobre la primera matriz.array_merge
O (∑ array_i, i! = 1) - no necesita iterar sobre la primera matriz+
(unión) O (n), donde n es el tamaño de la segunda matriz (es decir, array_first + array_second) - menos sobrecarga que array_merge ya que no tiene que volver a numerararray_replace
O (∑ array_i, para todo i)Al azar :
shuffle
En)array_rand
O (n): requiere una encuesta lineal.Obvio Big-O :
array_fill
En)array_fill_keys
En)range
En)array_splice
O (desplazamiento + longitud)array_slice
O (desplazamiento + longitud) u O (n) si longitud = NULLarray_keys
En)array_values
En)array_reverse
En)array_pad
O (pad_size)array_flip
En)array_sum
En)array_product
En)array_reduce
En)array_filter
En)array_map
En)array_chunk
En)array_combine
En)Me gustaría agradecer a Eureqa por facilitar la búsqueda de Big-O de las funciones. Es un programa gratuito increíble que puede encontrar la mejor función de ajuste para datos arbitrarios.
EDITAR:
Para aquellos que dudan de que las búsquedas de matriz PHP lo sean
O(N)
, he escrito un punto de referencia para probar eso (todavía son efectivosO(1)
para los valores más realistas).fuente
La explicación para el caso que describe específicamente es que las matrices asociativas se implementan como tablas hash, así que busque por clave (y, en consecuencia,
array_key_exists
) es O (1). Sin embargo, las matrices no están indexadas por valor, por lo que la única forma en el caso general de descubrir si existe un valor en la matriz es una búsqueda lineal. No hay sorpresa allí.No creo que haya documentación exhaustiva específica de la complejidad algorítmica de los métodos PHP. Sin embargo, si es una preocupación lo suficientemente grande como para justificar el esfuerzo, siempre puede consultar el código fuente .
fuente
Casi siempre quieres usar en
isset
lugar dearray_key_exists
. No estoy mirando lasarray_key_exists
partes internas, pero estoy bastante seguro de que es O (N) porque itera sobre todas y cada una de las teclas de la matriz, mientras queisset
intenta acceder al elemento usando el mismo algoritmo hash que se usa cuando se accede Un índice de matriz. Eso debería ser O (1).Un "problema" a tener en cuenta es este:
Tenía curiosidad, así que comparé la diferencia:
is_set:
0.132308959961 segundosarray_key_exists:
2.33202195168 segundosPor supuesto, esto no muestra la complejidad del tiempo, pero sí muestra cómo las 2 funciones se comparan entre sí.
Para probar la complejidad del tiempo, compare la cantidad de tiempo que lleva ejecutar una de estas funciones en la primera tecla y la última tecla.
fuente
$arrray[] = $append
vs.array_push($array, $append)
En segundo lugar, array_key_exists también diferencia entre valores no establecidos y valores nulos. For$a = array('fred' => null);
array_key_exists('fred', $a)
devolverá verdadero mientrasisset($['fred'])
que devolverá falso. Este paso adicional no es trivial y aumentará considerablemente el tiempo de ejecución.Si la gente tuviera problemas en la práctica con colisiones clave, implementaría contenedores con una búsqueda secundaria de hash o un árbol equilibrado. El árbol equilibrado daría O (log n) el peor comportamiento y O (1) promedio. caso (el hash mismo). La sobrecarga no vale la pena en la mayoría de las aplicaciones prácticas de memoria, pero tal vez hay bases de datos que implementan esta forma de estrategia mixta como su caso predeterminado.
fuente