2010-08-15 114 views
5

這是我在這裏的第一個問題:)如何在幾個數組中生成元素的組合?

我有一個數組的孩子,每個孩子都有獨特的價值,並希望得到所有可能的獨特組合。

陣列的數量是已知的,但可能隨時間而改變。

例如,

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

我應該怎麼做才能:

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

我試着嵌套循環這樣做,但我的邏輯是不是太強大了。

非常感謝,如果有人可以提供一些線索

+0

你總是會有一個矩形矩陣? – NullUserException 2010-08-15 01:12:17

+0

我的不好,數組的大小實際上變化很大。 – Fer 2010-08-15 23:44:53

回答

8

:需要一個輕微的修改在PHP < 5.3使用)

(上的在線翻譯example)操作方式:

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

靈感來自Mathematica的Outer的函數array_outer是這樣的:

/** 
* 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 2010-08-15 01:29:45

+0

我低下頭,但留下我的答案作爲一個快速和骯髒的解決方案的例子;) – Nicolas78 2010-08-15 01:30:09

+0

@Null不要擔心,因爲你刪除了答案,downvote會消失下一個代表重新計算:p – Artefacto 2010-08-15 01:34:09

0

latenight僞代碼:

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 

雖然支撐點可能會爲遞歸方法,如果陣列的數量變得更大或可動態改變

+0

@ nicolas78感謝您的意見。我最初採用了類似的方法,但可能有數十個陣列,因此很快就無法管理。乾杯! – Fer 2010-08-15 23:51:01

4

進行下面是一個遞歸方法這樣的:

$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

您的解決方案也可以。非常感謝您的幫助和時間 – Fer 2010-08-15 23:48:13

2

你真正尋找的是遍歷序列的方式:

000 
001 
010 
011 
100 
101 
110 
111 

如果我們不必假定每個輸入數組的大小是相同的,那也會很好。因此,如果我們減少第二陣列的1大小:

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

...我們希望該列的最大值由1下降:

000 
001 
100 
101 

這種抽象使得問題更容易想一想。你將如何迭代這個序列?在每次迭代中,您將最右邊的列增加1.如果這樣做會使其增加到最大值以下,請將其重置爲0並向左移動一列。現在你重複剛剛在最後一欄中做的事情。如果您無法增加此列,請將其重置爲0,向左移動,沖洗並重復。如果你一直移動並且沒有超過最大值就不能增加任何列,那麼就完成了。

我們可以包裝上面的邏輯在PHP迭代器:

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()是上述邏輯的實現。 reset()只是將每列重新設置爲0,並且current()將當前序列用作輸入的索引以返回當前值。

這是在行動(用 「陰天」 去掉,以顯示解決方案的通用性):

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

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

,其輸出:

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

您的解決方案也非常有效。非常感謝你! – Fer 2010-08-15 23:49:09

2

非常簡單的解決方案:

$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

感謝您的解決方案。我相信這個解決方案比上面的解決方案更快。 – machineaddict 2012-08-17 10:25:21

相關問題