2010-02-11 18 views
7

Tengo un número desconocido de matrices, cada una con un número desconocido de palabras. Quiero concatenar los valores de cada lista para que todas las variaciones posibles de las palabras se almacenen en una matriz final.Concatenar valores de n matrices en php

Por ejemplo, si array 1 contiene:

dog 
cat 

y la matriz 2 contiene:

food 
tooth 

y la matriz 3 contiene:

car 
bike 

me gustaría que la salida ser:

dog food car 
dog food bike 
dog tooth car 
dog tooth bike 
cat food car 
cat food bike 
cat tooth car 
cat tooth bike 

Puede haber más de 3 listas, y cada lista probablemente tendrá más de 2 palabras.

Me gustaría hacer esto en PHP.

Sé cómo hacerlo si conozco la cantidad de listas, aunque probablemente no sea el método más eficiente. Pero anidados foreach bucles funciona si conoce el número de matrices. ¿Qué pasa si no lo haces? Y cuáles son algunos métodos para resolver este problema que seguirán funcionando si, digamos, hay 100 arreglos de 100 palabras cada uno. ¿O 1000?

Gracias!

+0

¿usted también quiere * * perro, gato * *, * * comida para perros, diente de perro * *, * * comida para gatos, gato de dientes * * o sólo las combinaciones de todas las matrices ¿conjunto? – Gordon

+0

Simplemente la combinación de todas las matrices para este problema en particular, aunque eso también sería interesante de ver. – hookedonwinter

+0

Si usted quiere ser capaz de trabajar con 100 matrices de tamaño 100, tendrá serios problemas de memoria que tratan de generar realmente una matriz. La cantidad de combinaciones es un número realmente enorme. Este es un producto cartesiano. Hace falta un iterador. Este enfoque intercambia tiempo por espacio, es más lento pero le permite mantenerse dentro de los límites de memoria. Publicaba un enlace, pero sería al sitio web de un miembro activo aquí. Si él no viene solo y responde, publicaré el enlace. Pero de lo contrario no quiero robar su gloria. – goat

Respuesta

9

Usted puede poner todas las matrices de palabras en una matriz y utilizar un recursiva función como esta:

function concat(array $array) { 
    $current = array_shift($array); 
    if(count($array) > 0) { 
     $results = array(); 
     $temp = concat($array); 
     foreach($current as $word) { 
      foreach($temp as $value) { 
      $results[] = $word . ' ' . $value; 
      } 
     } 
     return $results;   
    } 
    else { 
     return $current; 
    } 
} 

$a = array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike')); 

print_r(concat($a)); 

que devuelve:

Array 
(
    [0] => dog food car 
    [1] => dog food bike 
    [2] => dog tooth car 
    [3] => dog tooth bike 
    [4] => cat food car 
    [5] => cat food bike 
    [6] => cat tooth car 
    [7] => cat tooth bike 
) 

pero supongo que se comporta mal para grandes matrices como la matriz de salida será muy grande.


Para evitar esto, puede generar las combinaciones directamente, usando un enfoque similar:

function concat(array $array, $concat = '') { 
    $current = array_shift($array); 

    $current_strings = array(); 

    foreach($current as $word) { 
      $current_strings[] = $concat . ' ' . $word; 
    } 

    if(count($array) > 0) { 
     foreach($current_strings as $string) { 
      concat($array, $string); 
     }  
    } 
    else { 
     foreach($current_strings as $string) { 
      echo $string . PHP_EOL; 
     } 
    } 
} 

concat(array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike'))); 

cual da:

dog food car 
dog food bike 
dog tooth car 
dog tooth bike 
cat food car 
cat food bike 
cat tooth car 
cat tooth bike 

Con este enfoque también es fácil de conseguir las "subcontaminaciones". Sólo tiene que insertar echo $string . PHP_EOL; antes concat($array, $string); y la salida es:

dog 
dog food 
dog food car 
dog food bike 
dog tooth 
dog tooth car 
dog tooth bike 
cat 
cat food 
cat food car 
cat food bike 
cat tooth 
cat tooth car 
cat tooth bike 
+0

Félix: funciona muy bien en matrices pequeñas. Yo sólo probé en 5 series de longitud 100, y tengo esto: 'error fatal: Permitido el tamaño de la memoria de 134217728 bytes agotado (tratado de asignar 11 bytes) en/Usuarios/QWERTY/- en línea 9' - No sé que tendré tantas palabras, entonces su solución probablemente funcionará bien para lo que estoy haciendo. Pero definitivamente tiene cierto retraso en matrices más grandes. ¡Gracias por la idea! – hookedonwinter

+0

@hookedonwinter: ¿Has probado también el enfoque iterativo? –

+0

@Felix aún no. Solo lo vi. ¡Gracias! – hookedonwinter

2

no he probado esto en enormes listas de palabras, pero es bastante rápido en las listas de tamaño moderado y no utiliza la recursividad, que creo que (por favor, corríjanme si me equivoco), probablemente está causando los problemas de límite de memoria:

$lines = array(''); 

foreach ($arrays as $array) { 

    $old_lines = $lines; 
    $lines = array(); 

    foreach ($array as $word) { 

    foreach ($old_lines as $line) { 

     $lines[] = trim($line .' '. $word); 

    } // foreach 

    } // foreach 

} // foreach 
+0

Supongo que el límite de memoria fue causado por la gran matriz de resultados, que sería lo mismo en su enfoque. Pero imprimir la línea no debería ser un problema. Me refiero a 100^5 elementos en conjunto es mucho;) –

+0

funciona impresionante en las matrices más pequeñas, no tan caliente en las grandes. ¡Me gusta, aunque! – hookedonwinter

2

Mi opinión

class Combinator 
{ 
    protected $words; 
    protected $combinator; 

    public function __construct($words, $combinator = null) 
    { 
     $this->words = $words; 
     $this->combinator = $combinator; 
    } 

    public function run($combo = '') 
    { 
     foreach($this->words as $word) { 
      if($this->combinator !== null) { 
       $this->combinator->run("$combo $word"); 
      } else { 
       echo "$combo $word", PHP_EOL; 
      } 
     } 
    } 
} 

$c = new Combinator(array('dog', 'cat'), 
        new Combinator(array('food', 'tooth'), 
            new Combinator(array('car', 'bike')))); 

$c->run(); 
5

puede enumerar los elementos de la r conjunto de esult, es decir, para cada número entero entre 0 .... (número de elementos) -1, puede indicar qué elemento devolver (es decirhay un orden natural). Para el ejemplo dado:

0 => array1[0], array2[0], array3[0] 
1 => array1[0], array2[0], array3[1] 
2 => array1[0], array2[1], array3[0] 
7 => array1[1], array2[1], array3[1] 

Todo lo que necesita es un (número entero) índice de n y una función que "traduce" el índice a la ésimo elemento del conjunto (naturales ordenado) n. Como solo necesita un número entero para almacenar el estado actual, el consumo de memoria no "explota" cuando tiene muchas/grandes matrices. Como dijo Chris en su comentario, usted cambia la velocidad (cuando usa juegos más pequeños) para un bajo consumo de memoria. (Aunque creo -La forma php es implemented- esto también es una solución rápida razonable.)

$array1 = array('dog', 'cat'); 
$array2 = array('food', 'tooth'); 
$array3 = array('car', 'bike'); 

function foo($key /* , ... */) { 
    $params = func_get_args(); 
    $rv = array(); 

    $key = array_shift($params); 
    $i=count($params); 

    while(0 < $i--) { 
    array_unshift($rv, $params[$i][ $key % count($params[$i]) ]); 
    $key = (int)($key/count($params[$i])); 
    } 
    return $rv; 
} 

for($i=0; $i<8; $i++) { 
    $a = foo($i, $array1, $array2, $array3); 
    echo join(', ', $a), "\n"; 
} 

Usted puede usar esto para poner en práctica, por ejemplo, un Iterator, un SeekableIterator o tal vez incluso una ArrayAccess (e invirtiendo de este modo el control en comparación con las soluciones recursivas, casi como un yield en Python o Ruby)

<?php 
$array1 = array('dog', 'cat', 'mouse', 'bird'); 
$array2 = array('food', 'tooth', 'brush', 'paste'); 
$array3 = array('car', 'bike', 'plane', 'shuttlecraft'); 
$f = new Foo($array1, $array2, $array3); 
foreach($f as $e) { 
    echo join(', ', $e), "\n"; 
} 

class Foo implements Iterator { 
    protected $data = null; 
    protected $limit = null; 
    protected $current = null; 

    public function __construct(/* ... */) { 
    $params = func_get_args(); 
    // add parameter arrays in reverse order so we can use foreach() in current() 
    // could use array_reverse(), but you might want to check is_array() for each element. 
    $this->data = array(); 
    foreach($params as $p) { 
     // <-- add: test is_array() for each $p --> 
     array_unshift($this->data, $p); 
    } 
    $this->current = 0; 
    // there are |arr1|*|arr2|...*|arrN| elements in the result set 
    $this->limit = array_product(array_map('count', $params)); 
    } 

    public function current() { 
    /* this works like a baseX->baseY converter (e.g. dechex()) 
     the only difference is that each "position" has its own number of elements/"digits" 
    */ 
    // <-- add: test this->valid() --> 
    $rv = array(); 
    $key = $this->current; 
    foreach($this->data as $e) { 
     array_unshift($rv, $e[$key % count($e)]); 
     $key = (int)($key/count($e)); 
    } 
    return $rv; 
    } 

    public function key() { return $this->current; } 
    public function next() { ++$this->current; } 
    public function rewind() { $this->current = 0; } 
    public function valid() { return $this->current < $this->limit; } 
} 

grabados

dog, food, car 
dog, food, bike 
dog, food, plane 
dog, food, shuttlecraft 
dog, tooth, car 
dog, tooth, bike 
[...] 
bird, paste, bike 
bird, paste, plane 
bird, paste, shuttlecraft 

(la secuencia parece estar bien ;-))

Cuestiones relacionadas