一、何为笛卡尔积
笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尔积(Cartesian product),又称直积,表示为X × Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员 。
例如:
假设集合A={a, b},集合B={0, 1, 2},
则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
熟悉数据库的小伙伴们,肯定也知道,这个概念在数据库中,也经常使用。
二、Java实现
1、固定个数list的笛卡尔积的实现(自我实现)
这种实现方式弊端很大,就是list个数固定,不符合实际生产需要,但也是提供了一种思路(递归法)。
也就是,不管list的个数大小是多少,都会与最后一个list中的所有元素,做笛卡尔积。
测试如下:
List<String> l1 = Arrays.asList("A", "B");
List<String> l2 = Arrays.asList("A", "B", "C", "D");
List<String> l3 = Arrays.asList("A", "B", "C");
List<List<String>> lists = new ArrayList<>();
List<String> item;
for (String s : l1) {
for (String s1 : l2) {
for (String s2 : l3) {
item = new ArrayList<>(List.of(s, s1, s2));
lists.add(item);
}
}
}
// 格式化输出,便于观察
for (int i = 0, len = l1.size() * l2.size() * l3.size(); i < len; i++) {
System.out.print(lists.get(i) + "\t");
if ((i + 1) % l3.size() == 0) {
System.out.println();
}
}
结果:
2、递归法(来自百度百科)
public static void main(String[] args) {
List<String> l1 = Arrays.asList("A", "B");
List<String> l2 = Arrays.asList("A", "B", "C", "D");
List<String> l3 = Arrays.asList("A", "B", "C");
List<List<String>> list = List.of(l1, l2, l3);
List<List<String>> result = new ArrayList<>();
descartes(list, result, 0, new ArrayList<>());
// 格式化输出,便于观察
for (int i = 0, len = l1.size() * l2.size() * l3.size(); i < len; i++) {
System.out.print(result.get(i) + "\t");
if ((i + 1) % l3.size() == 0) {
System.out.println();
}
}
}
/**
* description:笛卡尔乘积算法
* <p>
* 把一个List[ [1, 2], [3, 4], [a, b] ]
* 转化成
* List[ [1, 3, a], [1, 3, b], [1, 4, a], [1, 4, b], [2, 3, a], [2, 3, b], [2, 4, a], [2, 4, b] ]输出
* </p>
*
* @param dimvalue 要计算笛卡尔积的List集合(dimensionality维度)
* @param result 通过乘积转化后的数组
* @param layer 中间参数,即原List的长度
* @param curList 中间参数,保存每次计算的结果
*/
private static void descartes(List<List<String>> dimvalue,
List<List<String>> result,
int layer,
List<String> curList) {
// 判断 计算到第几个list了
if (layer < dimvalue.size() - 1) {
// 该层list为null或无元素,则直接进入下一层
List<String> layerList = dimvalue.get(layer);
if (layerList == null || layerList.isEmpty()) {
descartes(dimvalue, result, layer + 1, curList);
} else {
for (String item : layerList) {
List<String> temp = new ArrayList<>(curList);
temp.add(item);
descartes(dimvalue, result, layer + 1, temp);
}
}
} else if (layer == dimvalue.size() - 1) { // 到达最后一层list,也就是递归的终止条件
// 该层list为null或无元素,则直接把curList放入结果集中
List<String> endLayerList = dimvalue.get(layer);
if (endLayerList == null || endLayerList.isEmpty()) {
result.add(curList);
} else {
// 遍历最后一层list,与curList结合成新list,放入结果集result中
for (String item : endLayerList) {
List<String> temp = new ArrayList<>(curList);
temp.add(item);
result.add(temp);
}
}
} else {
// do nothing
}
}
结果:
3、Guava工具类的实现(来自Google)
此种方式最为简单直接,list个数是动态的,感兴趣的小伙伴们,可自行阅读源码 Lists.cartesianProduct()
测试如下:
List<String> l1 = Arrays.asList("A", "B");
List<String> l2 = Arrays.asList("A", "B", "C", "D");
List<String> l3 = Arrays.asList("A", "B", "C");
List<List<String>> lists = Lists.cartesianProduct(l1, l2, l3);
// 格式化输出,便于观察
for (int i = 0, len = l1.size() * l2.size() * l3.size(); i < len; i++) {
System.out.print(lists.get(i) + "\t");
if ((i + 1) % l3.size() == 0) {
System.out.println();
}
}
结果: