2010-02-11 8 views
7

Ich habe eine unbekannte Anzahl von Arrays, die jeweils eine unbekannte Anzahl von Wörtern enthält. Ich möchte die Werte von jeder Liste verketten, so dass alle möglichen Variationen der Wörter in einem endgültigen Array gespeichert werden.Verketten von Werten von n Arrays in PHP

Wenn beispielsweise Array 1 enthält:

dog 
cat 

und Array 2 enthält:

food 
tooth 

und Anordnung 3 enthält:

car 
bike 

ich die Ausgabe möchten be:

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

Es könnte mehr als 3 Listen geben, und jede Liste wird höchstwahrscheinlich mehr als 2 Wörter haben.

Ich möchte dies in PHP tun.

Ich weiß, wie es geht, wenn ich die Anzahl der Listen kenne, obwohl es wahrscheinlich nicht die ressourceneffizienteste Methode ist. Aber verschachtelte foreach Schleifen funktioniert, wenn Sie die Anzahl der Arrays kennen. Was, wenn du es nicht tust? Und was sind einige Methoden, um dieses Problem zu lösen, die immer noch funktionieren, wenn, sagen wir mal, es 100 Arrays von jeweils 100 Wörtern gibt. Oder 1000?

Danke!

+0

Willst du auch * Hund *, * Katze *, * Hundefutter *, * Hundezahn *, * Katzenfutter *, * Katzenzahn * oder nur die Kombinationen aller Felder kombiniert? – Gordon

+0

Nur die Kombination aller Arrays für dieses spezielle Problem, obwohl das auch interessant zu sehen wäre. – hookedonwinter

+0

Wenn Sie mit 100 Arrays der Größe 100 arbeiten möchten, haben Sie schwerwiegende Speicherprobleme, wenn Sie versuchen, ein Array zu generieren. Die Anzahl der Kombinationen ist eine wirklich enorme Zahl. Dies ist ein kartesisches Produkt. Sie benötigen einen Iterator. Bei diesem Ansatz wird Zeit für Speicherplatz vergeudet, da er langsamer ist, Sie aber innerhalb der Speichergrenzen bleiben können. Ich würde einen Link posten, aber es wäre hier auf der Website eines aktiven Mitglieds. Wenn er nicht alleine kommt und antwortet, werde ich den Link posten. Aber sonst will ich seinen Ruhm nicht stehlen. – goat

Antwort

9

Sie alle Word-Arrays in ein Array setzen und verwenden eine rekursive Funktion wie folgt aus:

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

Welche zurück:

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 
) 

Aber ich denke, das ist schlecht für große Arrays verhält sich wie Das Ausgabe-Array wird sehr groß sein.


Um dies zu umgehen, können Sie geben die Kombinationen direkt, einen ähnlichen Ansatz:

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'))); 

Welche gibt:

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

Mit diesem Ansatz kann auch leicht zu bekommen die "Unterkonkassationen". Legen Sie einfach echo $string . PHP_EOL; vor concat($array, $string); und der Ausgang ist:

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

Felix - Dies funktioniert hervorragend auf kleinen Arrays. Ich habe es gerade auf 5 Arrays der Länge 100 versucht, und habe dies: 'Schwerwiegender Fehler: Erlaubte Speichergröße von 134217728 Bytes erschöpft (versucht, 11 Bytes zuzuordnen) in/Benutzer/qwerty/- in Zeile 9' - Ich weiß es nicht dass ich so viele Wörter haben werde, also wird deine Lösung wahrscheinlich gut funktionieren für das, was ich mache. Aber definitiv hat etwas Verzögerung auf größeren Arrays. Danke für die Idee! – hookedonwinter

+0

@hookedonwinter: Hast du auch den iterativen Ansatz ausprobiert? –

+0

@Felix noch nicht. Ich habe es gesehen. Vielen Dank! – hookedonwinter

2

ich das auf riesigen Wortlisten nicht getestet haben, aber es ist ziemlich schnell auf mittlerer Größe Listen und nicht Rekursion nicht verwendet, was meiner Meinung nach (bitte korrigieren Sie mich, wenn ich falsch liege) wahrscheinlich ist, um die Speichergrenze Probleme verursacht:

$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

Ich denke, das Speicherlimit wurde durch das große Ergebnis-Array verursacht, was in Ihrem Ansatz das gleiche wäre. Aber das Drucken der Linie sollte kein Problem sein. Ich meine 100^5 Elemente in Array ist eine Menge;) –

+0

Works super auf kleineren Arrays, nicht so heiß auf den großen. Ich mag es aber! – hookedonwinter

2

mein nehmen

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

Sie die Elemente der r aufzählen Es wird für jede ganze Zahl zwischen 0 .... (Anzahl der Elemente) -1 angegeben, welches Element zurückgegeben werden soll (d. h.es gibt eine natürliche Reihenfolge). Für das gegebene Beispiel:

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] 

Alles, was Sie brauchen, ist ein (integer) Index n und eine Funktion, die der Index des n te Element des (natürlich bestellt) Satzes „übersetzt“. Da Sie nur eine ganze Zahl benötigen, um den aktuellen Zustand zu speichern, "explodiert" der Speicherverbrauch nicht, wenn Sie viele/große Arrays haben. Wie Chris in seinem Kommentar sagte, tauscht man die Geschwindigkeit (bei Verwendung kleinerer Sets) gegen geringen Speicherverbrauch. (Obwohl ich -der Weg php denken ist implemented- dies ist auch eine vernünftige schnelle Lösung.)

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

diese Weise können Sie zum Beispiel implementieren ein Iterator, ein SeekableIterator oder vielleicht sogar ein ArrayAccess (und damit das Umkehren der Steuerung im Vergleich zu den rekursiven Lösungen, fast wie ein yield in Python oder 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; } 
} 

druckt

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 

(die Sequenz scheint in Ordnung zu sein ;-))