2010-08-15 102 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