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