【问题标题】:How to get Cartesian product from multiple lists?如何从多个列表中获取笛卡尔积?
【发布时间】:2015-01-15 16:35:54
【问题描述】:

假设我有几个List<T>s,我会将它们放入另一个列表或其他集合中,所以我不知道我有多少list<T>,直到我打电话给List<List<T>>.size()

下面以List<Integer>为例:

list1=[1,2]
list2=[3,4]
list3=[5,6]
....
listn=[2*n-1,2n];

我怎样才能得到list1*list2*list3*...listn 的结果作为笛卡尔积?

例如:

list1*list2*list3

应该是:

[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]

【问题讨论】:

    标签: java list algorithm matrix cartesian-product


    【解决方案1】:

    您可以使用递归来实现它,递归的基本情况是当输入为空时返回空列表,否则处理剩余的元素。例如

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class CartesianProduct {
        public static <T> List<List<T>> calculate(List<List<T>> input) {
            List<List<T>> res = new ArrayList<>();
            if (input.isEmpty()) { // if no more elements to process
                res.add(new ArrayList<>()); // then add empty list and return
                return res;
            } else {
                // we need to calculate the cartesian product
                // of input and store it in res variable
                process(input, res);
            }
            return res; // method completes , return result
        }
    
        private static <T> void process(List<List<T>> lists, List<List<T>> res) {
            //take first element of the list
            List<T> head = lists.get(0);
            //invoke calculate on remaining element, here is recursion
            List<List<T>> tail = calculate(lists.subList(1, lists.size()));
    
            for (T h : head) { // for each head
                for (List<T> t : tail) { //iterate over the tail
                    List<T> tmp = new ArrayList<>(t.size());
                    tmp.add(h); // add the head
                    tmp.addAll(t); // and current tail element
                    res.add(tmp);
                }
            }
        }
    
        public static void main(String[] args) {
            //we invoke the calculate method
            System.out.println(calculate(Arrays.asList(
                    Arrays.asList(1, 2),
                    Arrays.asList(3, 4),
                    Arrays.asList(5, 6))));
        }
    }
    

    输出

    [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
    

    【讨论】:

      【解决方案2】:

      感谢@sol4me 使用尾递归的回答,这是另一个不使用尾递归的版本,但我认为更容易理解。

      public class CartesianProduct {
          public static <T> List<List<T>> calculate(List<List<T>> input) {
              List<List<T>> result = new ArrayList<List<T>>();
              if (input.isEmpty()) { // If input an empty list
                  // add empty list and return
                  result.add(new ArrayList<T>());
                  return result;
              } else {
                  // get the first list as a head
                  List<T> head = input.get(0);
                  // recursion to calculate a tail list
                  List<List<T>> tail = calculate(input.subList(1, input.size()));
                  // we merge every head element with every tail list.
                  for (T h : head) {
                      for (List<T> t : tail) {
                          List<T> resultElement = new ArrayList<T>();
                          resultElement.add(h);
                          resultElement.addAll(t);
                          result.add(resultElement);
                      }
                  }
              }
              return result;
          }
          public static void main(String[] args) {
              List<List<Integer>> bigList = Arrays.asList(
                      Arrays.asList(1, 2),
                      Arrays.asList(3, 4),
                      Arrays.asList(5, 6),
                      Arrays.asList(7, 8));
              System.out.println(calculate(bigList));
          }
      }
      

      【讨论】:

        【解决方案3】:

        使用嵌套循环的 map-and-reduce 方法

        1. 准备一个列表List&lt;List&lt;T&gt;&gt; 填充一个空值。此列表进一步用作中间结果的存储和最终结果。

        2. 将传入列表List&lt;List&lt;T&gt;&gt;中的数据依次追加到中间结果中,得到最终结果。从示意图上看,这个迭代过程如下所示:

          result0: [[]]
            list1: [1,2]
          -------
          result1: [[1],[2]]
            list2: [3,4]
          -------
          result2: [[1,3],[1,4],[2,3],[2,4]]
            list3: [5,6]
          -------
          result3: [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
          

        Try it online!

        /**
         * @param lists an arbitrary number of lists
         * @param <T>   the type of the elements
         * @return the Cartesian product
         */
        public static <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
            // check if incoming data is not null
            if (lists == null) return Collections.emptyList();
            // Cartesian product, intermediate result
            List<List<T>> cp = Collections.singletonList(Collections.emptyList());
            // iterate through incoming lists
            for (List<T> list : lists) {
                // non-null and non-empty lists
                if (list == null || list.size() == 0) continue;
                // intermediate result for next iteration
                List<List<T>> next = new ArrayList<>();
                // rows of current intermediate result
                for (List<T> row : cp) {
                    // elements of current list
                    for (T el : list) {
                        // new row for next intermediate result
                        List<T> nRow = new ArrayList<>(row);
                        nRow.add(el);
                        next.add(nRow);
                    }
                }
                // pass to next iteration
                cp = next;
            }
            // Cartesian product, final result
            return cp;
        }
        
        public static void main(String[] args) {
            List<List<Integer>> lists = prepareLists(3);
            List<List<Integer>> cp = cartesianProduct(lists);
            // output without spaces
            System.out.println(lists.toString().replace(" ", ""));
            System.out.println(cp.toString().replace(" ", ""));
        }
        
        // supplementary method, prepares lists for multiplication
        public static List<List<Integer>> prepareLists(int n) {
            List<List<Integer>> lists = new ArrayList<>(n);
            for (int i = 1; i <= n; i++)
                lists.add(Arrays.asList(i * 2 - 1, i * 2));
            return lists;
        }
        

        输出:

        [[1,2],[3,4],[5,6]]
        [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
        

        另见:Generate all combinations from multiple lists

        【讨论】:

          猜你喜欢
          • 2023-02-19
          • 2015-07-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-03
          • 2012-03-24
          • 2023-02-19
          相关资源
          最近更新 更多