笛卡尔/组合算法(保持秩序)

时间:2012-04-05 00:09:34

标签: algorithm combinations cartesian-product

由于我不太了解这些类型的算法的语言(即如何谷歌这个),我只是展示我正在寻找的东西:

我有三个数组(源数组的长度不相等):

$array1 = array('A', 'B', 'C', 'D');
$array2 = array('x', 'y', 'z');
$array3 = array('1', '2', '3');

我希望这些数组的所有可能组合在哪里:

  • 每个源数组中只能有一个元素。
  • array1,array2,array3的顺序永远不会被破坏(ABC总是在xyz总是在123之前出现。)

结果将是:

array(
  array('A', 'x', '1'),
  array('A', 'x', '2'),
  array('A', 'x', '3'),
  array('A', 'y', '1'),
  // etc ...

  // But I also need all the partial sets, as long as the rule about
  // ordering isn't broken i.e.:
  array('B'),
  array('B', 'x'),
  array('B', 'x', '1'),
  array('x'),
  array('x', '1'),
  array('1'),
);

结果的顺序对我来说无关紧要。

在php中工作,但类似的语言或伪代码当然没问题。或者我只是简单介绍一下我应该关注的特定类型的排列/组合算法。

2 个答案:

答案 0 :(得分:2)

我会说这些是笛卡尔积。生成它们非常容易。

  • 对于固定数量的数组(在Perl中):

    for my $a(@arrayA) {
      for my $b(@arrayB) {
        push @result, [$a, $b];
      }
    }
    
  • 一般程序:假设@partialA1 x A2 x ... x An的笛卡尔积的数组,我们想要A1 x ... x An x An+1

    for my $a(@partial) {
      for my $b(@An_plus_1) {
        push @result, [@$a, $b];
      }
    }
    

    这显然需要迭代所有数组。

现在,你想要省略集合中的一些元素,你只需稍微扭曲一下。在第一种方法中,您可以向每个数组添加另一个元素(undef是明显的选择,但任何事情都可以),然后在结果集中过滤掉这些元素。在第二种方法中,它更容易:您只需将@partialmap { [$_] } @An_plus_1添加到结果中(或者,在英语中,由A1 x ... x An的部分笛卡尔积所得的所有集合加上单个元素集由新集合的元素组成。)

答案 1 :(得分:0)

使用RBarryYoung的提示,这是生成它们的最短方式,bash(和sed,删除D,w和4):

echo {A..D}{w..z}{1..4} | sed 's/[Dw4]//g'
  

A1 A2 A3 A Ax1 Ax2 Ax3 Ax Ay1 Ay2 Ay3 Ay Az1 Az2 Az3 Az   B1 B2 B3 B Bx1 Bx2 Bx3 Bx By1 By2 By3 By Bz1 Bz2 Bz3 Bz   C1 C2 C3 C Cx1 Cx2 Cx3 Cx Cy1 Cy2 Cy3 Cy Cz1 Cz2 Cz3 Cz    1 2 3 x1 x2 x3 x y1 y2 y3 y z1 z2 z3 z

另一种简单的方法是SQL,它默认使用它:

SELECT upper, lower, num 
FROM uppers, lowers, numbers
WHERE upper in ('A', 'B', 'C', ' ')
AND lower in (' ', 'x', 'y', 'z')
AND (number in (1, 2, 3) OR number IS NULL);

如果你的表只包含'A,B,C,'和'x,y,z,'和'1,2,3',那就更短了:

SELECT upper, lower, num 
FROM uppers, lowers, numbers;

除了笛卡儿产品之外,这个组合的另一个词是跨产品

对于未知数量的未知大小的Lists / Sequences /其他集合,我建议使用迭代器 - 如果PHP有这样的东西。这是Scala中的一个实现:

  class CartesianIterator (val ll: Seq[Seq[_]]) extends Iterator [Seq[_]] {
    var current = 0
    def size = ll.map (_.size).product
    lazy val last: Int = len

    def get (n: Int, lili: Seq[Seq[_]]): List[_] = lili.length match {
      case 0 => List () 
      case _ => {
        val inner = lili.head 
        inner (n % inner.size) :: get (n / inner.size, lili.tail) 
      }
    }

    override def hasNext () : Boolean = current != last
    override def next (): Seq[_] = {
      current += 1
      get (current - 1, ll) 
    }
  }

  val ci = new CartesianIterator (List(List ('A', 'B', 'C', 'D', ' '), List ('x', 'y', 'z', ' '), List (1, 2, 3, 0)))
  for (c <- ci) println (c)

List(A, x, 1)
List(B, x, 1)
List(C, x, 1)
List(D, x, 1)
List( , x, 1)
List(A, y, 1)
List(B, y, 1)
...
List( , z, 0)
List(A,  , 0)
List(B,  , 0)
List(C,  , 0)
List(D,  , 0)
List( ,  , 0)

可以使用包装器从输出中删除'0'和''。