5 Stimmen

Wie kann man Kombinationen von Elementen in mehreren Arrays erzeugen?

Dies ist meine erste Frage hier :)

Ich habe ein Array mit einer Reihe von Array Kinder, die jeweils mit eindeutigen Werten und möchte alle möglichen eindeutigen Kombinationen dieser Werte zu erhalten.

Die Anzahl der Felder 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 sollte ich tun, um zu bekommen:

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 machen, aber meine Logik ist nicht allzu stark.

Ich bin sehr dankbar, wenn jemand etwas Licht ins Dunkel bringen kann.

8voto

Artefacto Punkte 93200

( Hinweis (für die Verwendung in PHP < 5.3 ist eine kleine Änderung erforderlich)

Tun Sie dies ( Beispiel über einen Online-Dolmetscher):

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

Die Funktion array_outer , inspiriert von Mathematica's 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;
}

4voto

Obto Punkte 1377

Hier ist ein rekursiver Ansatz zu diesem Thema:

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

2voto

Ed Mazur Punkte 2902

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

000
001
010
011
100
101
110
111

Es wäre auch schön, wenn wir nicht davon ausgehen müssten, dass die Größe der einzelnen Eingabefelder gleich ist. Wenn wir also die Größe des zweiten Arrays um 1 verringern:

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

...wollen wir, dass der Maximalwert für diese Spalte um 1 verringert wird:

000
001
100
101

Durch diese Abstraktion wird das Problem leichter zu verstehen. Wie würden Sie diese Folge iterieren? Bei jeder Iteration erhöhen Sie die Spalte ganz rechts um 1. Wenn sie dadurch über ihr Maximum hinaus wachsen würde, setzen Sie sie auf 0 zurück und gehen eine Spalte nach links. Nun wiederholen Sie das, was Sie gerade mit der letzten Spalte gemacht haben. Wenn Sie auch diese Spalte nicht erhöhen können, setzen Sie sie auf 0 zurück, gehen nach links, spülen und wiederholen. Wenn Sie den ganzen Weg zurückgelegt haben und keine Spalte erhöhen konnten, ohne den Maximalwert zu überschreiten, sind Sie fertig.

Wir können die obige Logik in einen PHP-Iterator verpacken:

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() ist die Umsetzung der obigen Logik. 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 sie in Aktion zu sehen (wobei "wolkig" entfernt wurde, um die Allgemeinheit der Lösung zu verdeutlichen):

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

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

Und seine Leistung:

blue, sunny, sweet
blue, sunny, acid
red, sunny, sweet
red, sunny, acid

2voto

Biser Antonov Punkte 151

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

0voto

Nicolas78 Punkte 5093

Latenight Pseudocode:

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 starkes Argument für einen rekursiven Ansatz sprechen könnte, wenn die Anzahl der Arrays größer wird oder sich dynamisch ändern kann

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X