2009-12-21 32 views
13

¿Cuál sería la forma más eficiente de seleccionar cada enésimo elemento de una matriz grande? ¿Hay una manera "inteligente" de hacerlo o está girando de la única manera?Seleccionar cada enésimo elemento de una matriz

Algunos puntos a considerar:

  • La matriz es bastante grande, con 130 000 unidades
  • tengo que seleccionar cada artículo 205a
  • Los artículos no están indexados numéricamente, por lo que no va a funcionar for($i = 0; $i <= 130000; $i += 205)

hasta ahora, este es el método más eficiente que he llegado con:

$result = array(); 
$i = 0; 
foreach($source as $value) { 

    if($i >= 205) { 
     $i = 0; 
    } 

    if($i == 0) { 
     $result[] = $value; 
    } 

    $i++; 
} 

O lo mismo con módulo:

$result = array(); 
$i = 0; 
foreach($source as $value) { 
    if($i % 205 == 0) { 
     $result[] = $value; 
    } 
    $i++; 
} 

Estos métodos pueden ser bastante lento, ¿hay alguna manera de mejorar? ¿O acabo de dividir los pelos aquí?

EDITAR

buenas respuestas de todo con explicaciones adecuadas, trataron de escoger el más adecuado como la respuesta aceptada. ¡Gracias!

+0

Eso me parece razonable: ¿está seguro de que el código está causando cuellos de botella? Si no, ¡perfúlalo para ver! ¿Cuánto tiempo se tarda? –

+0

@Dominic, esto no es tanto un cuello de botella, solo un problema interesante para el que no pude encontrar una solución adecuada. No crea que una respuesta 'correcta' afeitaría más de unos pocos milisegundos de tiempo de ejecución, pero sería bueno saberlo. :) –

Respuesta

13

Un bucle foreach proporciona la iteración más rápida sobre su matriz grande basada en pruebas de comparación. Me quedaría con algo similar a lo que tienes a menos que alguien desee resolver el problema con loop que se desenrolla.

Esta respuesta debe ejecutarse más rápido.

$result = array(); 
$i = 0; 
foreach($source as $value) { 
    if ($i++ % 205 == 0) { 
     $result[] = $value; 
    } 
} 

no tengo tiempo para probar, pero que podría ser capaz de utilizar una variación de @ de Haim solución si el índice primero numéricamente la matriz. Vale la pena probar para ver si puede recibir cualquier ganancia por encima de mi solución anterior:

$result = array(); 
$source = array_values($source); 
$count = count($source); 
for($i = 0; $i < $count; $i += 205) { 
    $result[] = $source[$i]; 
} 

Esto dependería en gran medida de cómo optimizar las array_values ​​función es. Podría funcionar muy bien.

+0

Gran explicación y en realidad una forma más rápida de lograr lo que traté de hacer (solo levemente, pero más rápido de todos modos). ¡Gracias! –

+0

@Tatu, después de una prueba de comparación rápida en array_push y [] en una matriz de prueba de 200,000 elementos que encontré [] para ejecutar aproximadamente el doble de rápido. Modifiqué mi respuesta y sugeriría usar la tarea de la manera en que inicialmente la tenías. –

+1

@Tatu, analicé rápidamente los dos métodos anteriores y encontré que el segundo es más rápido cuando la matriz de arranque tiene en promedio más de 50,000 resultados. Sin embargo, solo tuve tiempo de probar comenzando con matrices indexadas numéricamente generadas por array_fill() y range(). –

6

me recomiendan para el uso array_slice

$count = count($array) ; 
for($i=205;$i<$count;$i+=205){ 
    $result[] = array_slice($array,$i,1); 
} 

Si la matriz fue indexado numéricamente, esto sería muy rápido:

$count = count($array) ; 
for($i=205;$i<$count;$i+=205){ 
    $result[] = $array[$i]; 
} 
+1

Al menos la sintaxis es mucho más clara, ¿alguna idea podría mejorar el rendimiento? –

+1

Para bucles son lentos. Tu conteo() también es ridículamente lento. –

+0

Si sacamos el conteo del ciclo, esto aceleraría sustancialmente. –

0

no se puede mover el puntero, al parecer, más de de a una vez Me gustaría utilizar este personal:

reset($source); 
$next = true; 
while($next === true){ 
    $result[] = current($source); 
    for(i=0;i<205;i++){ 
     $next = next($source); 
    } 
} 

Si alguien puede encontrar una función que puede mover el puntero de la lista algo más que un paso a la vez, usted tendrá una mejor respuesta. Creo que esto es bueno sin embargo.

+0

Tiene razón, mover la punta del puntero 205 posiciones a la vez sería la solución, de lo contrario, todos son exactamente lo mismo con una sintaxis diferente, supongo. –

+1

Ver mi respuesta. ArrayIterator :: seek puede mover el puntero a la posición – Gordon

+0

¿Qué pasa si hay valores que no se evalúan como * verdadero *? – Gumbo

7

Trate ArrayIterator::seek()

Además, el uso de un nuevo Spl datastructures podría dar mejores resultados que el uso de matrices de civil.

+0

Veremos eso, solo razón por la que no acepté esto ya que la respuesta correcta fue que tendría que cambiar mi base de código significativamente para admitir ArrayObjects de manera eficiente. Pero notado y más. –

1

Si esto realmente es un cuello de botella, es posible que desee considerar repensar su diseño para hacerlo indexado numéricamente.

EDITAR: O crea y mantiene una matriz separada con solo 205 elementos (que se actualiza en la inserción o algo así).

-1

Puede usar array_keys para trabajar solo en las teclas de matriz.

$keys = array_keys($array); 
for ($i=0, $n=min(count($keys), 130000); $i<$n; $i += 205) { 
    $result[] = $array[$keys[$i]]; 
} 
+3

Si el recorrido de la matriz es un cuello de botella, ¿no será la recuperación de todas las claves? – xtofl

+0

@xtofl: las matrices en PHP no son matrices reales como en otros idiomas. Están más bien implementados con una tabla hash. Y las claves se almacenan por separado, probablemente en una lista vinculada. – Gumbo

2

Creo que la solución a este problema no radica en ninguna sintaxis PHP, sino en el diseño de su código.

Puede hacer que la matriz esté indexada numéricamente (puede que no sea plausible para su aplicación), realizar un seguimiento de cada 205o elemento, o solo buscar la matriz una vez (almacenar en caché una lista de cada 205o elemento).

En mi opinión, hacer un seguimiento de cada 205º elemento sería más fácil de implementar. Simplemente mantendrá un recuento de todos los elementos en una base de datos o algo así, y cada vez que se agrega un elemento, verifique el módulo del recuento. Si tiene otro 205º elemento, agréguelo al conjunto. Sin embargo, cuando se eliminan los elementos, esto sería más complicado. Es posible que deba volver a verificar toda la matriz para realinear todos sus 205 elementos.

Hacer esto sería más simple si pudiera comenzar en el elemento eliminado y seguir adelante, pero de nuevo esto solo funcionaría para matrices indexadas numéricamente, y si eso fuera cierto, no tendría que avanzar en absoluto, harías un poco de matemáticas para volver a resolverlo.

  • índices numéricos - mejor solución a largo plazo, pero más difícil de implementar
  • Hacer un seguimiento - más fácil de implementar, pero que tendría que ensuciarse de nuevo al eliminar elementos
  • elementos de almacenamiento en caché - probablemente también deberías hacer esto para las otras dos soluciones, pero por sí solo sería rápido hasta que se modificara la matriz, en cuyo caso probablemente tendrías que volver a hacerlo.
0
  • Crear una matriz de dos dimentional [205] [N]
  • Cargar datos en array
  • Acceso elemento 205a para cada N

puede sonar tonto pero, por definición, es más rápido ya que accede a las ubicaciones de memoria directamente y no realiza ninguna comparación.

Cuestiones relacionadas