2010-08-15 11 views
5

Dies ist meine erste Frage hier :)Wie generiert man Kombinationen der Elemente in mehreren Arrays?

Ich habe ein Array mit einer Reihe von Array-Kinder, jedes mit eindeutigen Werten und möchte alle möglichen einzigartigen Kombinationen dieser Werte erhalten.

Die Anzahl der Arrays ist bekannt, kann sich aber im Laufe der Zeit ändern.

Zum Beispiel

array(
    [0] => array([0]=>'blue',[1]=>'red'), 
    [1] => array([0]=>'sunny',[1]=>'cloudy'), 
    [2] => array([0]=>'sweet',[1]=>'acid'); 

Was soll ich nun tun:

array(
    [0] => array([0]=>'blue',[1]=>'sunny',[2]=>'sweet'), 
    [1] => array([0]=>'blue',[1]=>'sunny',[2]=>'acid'), 
    [2] => array([0]=>'blue',[1]=>'cloudy',[2]=>'sweet'), 
    [3] => array([0]=>'blue',[1]=>'cloudy',[2]=>'acid'), 
    [4] => array([0]=>'red',[1]=>'sunny',[2]=>'sweet'), 
    [5] => array([0]=>'red',[1]=>'sunny',[2]=>'acid'), 
    [6] => array([0]=>'red',[1]=>'cloudy',[2]=>'sweet'), 
    [7] => array([0]=>'red',[1]=>'cloudy',[2]=>'acid')); 

Ich habe versucht, es mit verschachtelten Schleifen zu tun, aber meine Logik nicht zu stark ist.

Sehr viel geschätzt, wenn jemand etwas Licht

+0

Haben Sie immer eine rechteckige Matrix? – NullUserException

+0

Meine schlechte, die Größe der Arrays variiert tatsächlich ziemlich viel. – Fer

Antwort

8

Schuppen können (Hinweis: benötigt eine leichte Modifikation in PHP zu verwenden < 5,3)

dies tun (example auf einem Online-Interpreter):

$f = function() { return func_get_args(); }; 
$res = array_outer($f, 
    array("blue", "red"), 
    array("sunny", "cloudy"), 
    array("sweet", "acid")); 

Die Funktion array_outer, inspiriert in Mathematica Outer, ist dies:

/** 
* A generalization of the outer product, forming all the possible 
* combinations of the elements of any number of arrays and feeding 
* them to $f. 
* The keys are disregarded 
**/ 
function array_outer($f, array $array1) { 
    $res = array(); 
    $arrays = func_get_args(); 
    array_shift($arrays); 
    foreach ($arrays as $a) { 
     if (empty($a)) 
      return $res; 
    } 

    $num_arrays = count($arrays); 
    $pos = array_fill(0, $num_arrays, 0); 
    while (true) { 
     $cur = array(); 
     for ($i = 0; $i < $num_arrays; $i++) { 
      $cur[] = $arrays[$i][$pos[$i]]; 
     } 
     $res[] = call_user_func_array($f, $cur); 
     for ($i = $num_arrays-1; $i >= 0; $i--) { 
      if ($pos[$i] < count($arrays[$i]) - 1) { 
       $pos[$i]++; 
       break; 
      } else { 
       if ($i == 0) 
        break 2; 
       $pos[$i] = 0; 
      } 
     } 
    } 
    return $res; 
} 
+1

+1 Apesar dos pesares;) – NullUserException

+0

Ich beuge meinen Kopf in Schande, aber lassen Sie meine Antwort als ein Beispiel für eine schnelle und schmutzige Lösung;) – Nicolas78

+0

@Null Keine Sorge, da Sie die Antwort gelöscht haben, wird der Downvote aufgehen die nächste Rep-Neuberechnung: p – Artefacto

0

latenight Pseudo-Code:

result = [] 
counter = 0 
for i in length(array[0]): 
    for j in length(array[1]): 
    for k in length(array[2]):  
     result[counter] = aray(0: array[0][i], 1: array[1][j], 2: array[2][k]) 
     counter+=1 

obwohl ein starker Punkt könnte für eine rekursive Ansatz gemacht werden, wenn die Anzahl der Arrays wird größer in Gang bringen oder sie können dynamisch

+0

@ nicolas78 Danke für Ihre Eingabe. Ich ging ursprünglich für einen ähnlichen Ansatz, aber es kann Dutzende von Arrays geben, so dass es schnell unüberschaubar wird. Prost! – Fer

4

ändern Hier ist eine rekursive Ansatz dies:

$arr = array(
      0 => array(0 =>'blue', 1 =>'red'), 
      1 => array(0 =>'sunny', 1 =>'cloudy'), 
      2 => array(0 =>'sweet', 1 =>'acid') 
     ); 

$combinations = array(); 
getArrayCombinations($arr, $combinations); 
echo '<pre>';print_r($combinations); 

/** 
* Creates an array with all possible combinations 
* @param array main_array - Array to find all the possible combinations of 
* @param array combinations - Array to store the resulting array in 
* @param array batch 
* @param int index 
*/ 
function getArrayCombinations($main_array, &$combinations, $batch=array(), $index=0) 
{ 
    if ($index >= count($main_array)) 
     array_push($combinations, $batch); 
    else 
     foreach ($main_array[$index] as $element) 
     { 
      $temp_array = $batch; array_push($temp_array, $element); 
      getArrayCombinations($main_array, $combinations, $temp_array, $index+1); 
     } 
} 
+0

Ihre Lösung funktioniert auch gut. Vielen Dank für Ihre Hilfe und Zeit – Fer

2

Was sind Sie wirklich suchen, ist eine Möglichkeit, Sequenzen iterieren:

000 
001 
010 
011 
100 
101 
110 
111 

Es wäre auch schön, wenn wir nicht die Annahme machen müssten, dass die Größe jedes Eingangsarrays gleich ist. Also, wenn wir die Größe des zweiten Feldes verringern, indem 1:

array(
    [0] => array([0]=>'blue',[1]=>'red'), 
    [1] => array([0]=>'sunny'), 
    [2] => array([0]=>'sweet',[1]=>'acid'); 

... wollen wir den maximalen Wert für diese Spalte um 1 zu verringern:

000 
001 
100 
101 

Diese Abstraktion macht das Problem leichter nachdenken über. Wie würdest du diese Sequenz wiederholen? Bei jeder Iteration erhöhen Sie die Spalte ganz rechts um 1. Wenn Sie sie über ihr Maximum hinaus erhöhen würden, setzen Sie sie auf 0 zurück und verschieben Sie eine Spalte nach links. Jetzt wiederholst du, was du gerade in der letzten Spalte getan hast. Wenn Sie diese Spalte auch nicht vergrößern können, setzen Sie sie auf 0 zurück, bewegen Sie sie nach links, spülen Sie sie und wiederholen Sie sie. Wenn Sie sich den ganzen Weg bewegen und keine Spalte vergrößern konnten, ohne über ihr Maximum hinauszugehen, sind Sie fertig.

Wir können die obige Logik in einem PHP Iterator wickeln:

class Sequence implements Iterator { 

    private $input; 

    private $hasNext; 
    private $positions; 

    public function __construct(array $input) { 
     $this->input = $input; 
    } 

    public function rewind() { 
     $this->hasNext = true; 
     $this->positions = array(); 
     for ($i = 0; $i < count($this->input); $i++) { 
      $this->positions[$i] = 0; 
     } 
    } 

    public function valid() { 
     return $this->hasNext; 
    } 

    public function current() { 
     $current = array(); 
     for ($i = 0; $i < count($this->positions); $i++) { 
      $current[] = $this->input[$i][$this->positions[$i]]; 
     } 
     return $current; 
    } 

    public function key() {} 

    public function next() { 
     for ($i = count($this->positions) - 1; $i >= 0; $i--) { 
      if ($this->positions[$i] < count($this->input[$i]) - 1) { 
       $this->positions[$i]++; 
       break; 
      } else { 
       $this->positions[$i] = 0; 
       $this->hasNext = $i !== 0; 
      } 
     } 
    } 

} 

next() die Umsetzung der obigen Logik ist. reset() setzt einfach jede Spalte auf 0 zurück und current() verwendet die aktuelle Sequenz als die Indizes der Eingabe, um die aktuellen Werte zurückzugeben.

Hier ist es in Aktion (mit "wolkigen" entfernt, um die Allgemeingültigkeit der Lösung zu zeigen):

$input = array(
    array('blue', 'red'), 
    array('sunny'), 
    array('sweet', 'acid') 
); 

$lst = new Sequence($input); 
foreach ($lst as $elt) { 
    print(implode(', ', $elt) . "\n"); 
} 

und seine Ausgabe:

blue, sunny, sweet 
blue, sunny, acid 
red, sunny, sweet 
red, sunny, acid 
+0

Ihre Lösung ist auch sehr gültig. Vielen Dank! – Fer

2

Sehr einfache Lösung:

$arr = array(
    array('a', 'b', 'c'), 
    array('x', 'y', 'z'), 
    array('1', '2') 
); 

$result = array(); 
foreach ($arr as $a) { 
    if (empty($result)) { 
     $result = $a; 
     continue; 
    } 

    $res = array(); 
    foreach ($result as $r) { 
     foreach ($a as $v) { 
      $res[] = array_merge((array)$r, (array)$v); 
     } 
    } 

    $result = $res; 
} 

var_dump($result); 
+0

Danke für diese Lösung. Ich glaube, dass diese Lösung schneller ist als die obige. – machineaddict

Verwandte Themen