1、笛卡尔积定义(摘自百度百科)

笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尓积(Cartesian product),又称直积,表示为X × Y,
第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员 [1]  。


假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。

    类似的例子有,如果A表示某学校学生的集合,B表示该学校所有课程的集合,则A与B的笛卡尔积表示所有可能的选课情况。
A表示所有声母的集合,B表示所有韵母的集合,那么A和B的笛卡尔积就为所有可能的汉字全拼。

设A,B为集合,用A中元素为第一元素,B中元素为第二元素构成有序对,所有这样的有序对组成的集合叫做A与B的笛卡尔积,记作AxB.
笛卡尔积的符号化为:
    A×B={(x,y)|x∈A∧y∈B}
    例如,A={a,b}, B={0,1,2},则
        A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
        B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}

2、分析:

设集合A = [a,b] , B=[0,1,2]
那么求AxB的结果按照数组角标来表示为
[(A[0],B[0]),(A[0],B[1]),(A[0],B[2]),(A[1],B[0]),(A[1],B[1]),(A[1],B[2])]

如果在加一个集合C=[x,y,z]
求AxBxC那么结果等于
= AxB的结果xC
= [
    (A[0],B[0]),
    (A[0],B[1]),
    (A[0],B[2]),
    (A[1],B[0]),
    (A[1],B[1]),
    (A[1],B[2])
  ]xC
= [
    (A[0],B[0],C[0]),(A[0],B[0],C[1]),(A[0],B[0],C[2]),
    (A[0],B[1],C[0]),(A[0],B[1],C[1]),(A[0],B[1],C[2]),
    (A[0],B[2],C[0]),(A[0],B[2],C[1]),(A[0],B[2],C[2]),
    (A[1],B[0],C[0]),(A[1],B[0],C[1]),(A[1],B[0],C[2]),
    (A[1],B[1],C[0]),(A[1],B[0],C[1]),(A[1],B[0],C[2]),
    (A[1],B[2],C[0]),(A[1],B[0],C[1]),(A[1],B[0],C[2]),
  ]
 = [
    (A[0],B[0],C[0]),
    (A[0],B[0],C[1]),
    (A[0],B[0],C[2]),
    (A[0],B[1],C[0]),
    (A[0],B[1],C[1]),
    (A[0],B[1],C[2]),
    (A[0],B[2],C[0]),
    (A[0],B[2],C[1]),
    (A[0],B[2],C[2]),
    (A[1],B[0],C[0]),
    (A[1],B[0],C[1]),
    (A[1],B[0],C[2]),
    (A[1],B[1],C[0]),
    (A[1],B[0],C[1]),
    (A[1],B[0],C[2]),
    (A[1],B[2],C[0]),
    (A[1],B[0],C[1]),
    (A[1],B[0],C[2]),
  ]

  观察数组角标的变化可以找到一个规律:
      就像数数一样逢数组length-1进位,数完了结束。

  实现思路:
    假设将所有需要求笛卡尔积的集合都放到一个大集合中[A,B,C]
    1.取角标指针CIndex用来表示进位进到哪了
    比如从C开始数 初始角标执行值 = 2
    C进位了 将指针改为 1
    B进位了 将指针改为 0
    A进位了 已经无处可进,此时运算结束。

    2.取一组指针CounterMap分别用来记录A,B,C数到哪了
    CounterMap['C'] = 0 ..=1 ..=2
    ...
    CIndex只向前走,而CounterMap循环走

3. 算法java实现

public class CartesianArith {

    public <T> List<List<T>> cartesianProduct(T[]... sets) {
        if (sets == null || sets.length == 0) {
            return Collections.emptyList();
        }
        int total = 1;
        //声明进位指针cIndex
        int cIndex = sets.length - 1;
        //声明counterMap(角标 - counter)
        int[] counterMap = new int[sets.length];
        for (int i = 0; i < sets.length; i++) {
            counterMap[i] = 0;
            total *= (sets[i] == null || sets[i].length == 0 ? 1 : sets[i].length);
        }
        List<List<T>> rt = new ArrayList<>(total);
        //开始求笛卡尔积
        while (cIndex >= 0) {
            List<T> element = new ArrayList<>(sets.length);
            for (int j = 0; j < sets.length; j++) {
                T[] set = sets[j];
                //忽略空集
                if (set != null && set.length > 0) {
                    element.add(set[counterMap[j]]);
                }
                //从末位触发指针进位
                if (j == sets.length - 1) {
                    if (set == null || ++counterMap[j] > set.length - 1) {
                        //重置指针
                        counterMap[j] = 0;
                        //进位
                        int cidx = j;
                        while (--cidx >= 0) {
                            //判断如果刚好前一位也要进位继续重置指针进位
                            if (sets[cidx] == null || ++counterMap[cidx] > sets[cidx].length - 1) {
                                counterMap[cidx] = 0;
                                continue;
                            }
                            break;
                        }
                        if (cidx < cIndex) {
                            //移动进位指针
                            cIndex = cidx;
                        }
                    }
                }
            }
            if (element.size() > 0) {
                rt.add(element);
            }
        }
        return rt;
    }

    @Test
    public void testCartesianProduct2() {
        System.out.println(Arrays.deepToString(
                cartesianProduct(
                        new String[]{"a", "b"}, new String[]{"0", "1", "2"}, new String[]{"0", "1", "2"}, new String[]{"0", "1", "2"}, new String[]{"0", "1", "2"}, new String[]{"0", "1", "2"}
                ).toArray()));
    }

}