【问题标题】:How to recursively get all combinations in Java?如何递归获取Java中的所有组合?
【发布时间】:2015-10-20 14:04:23
【问题描述】:

在 Java 中,使用递归函数从多组候选中获取所有元素组合的最佳方法是什么?

一般而言,候选集的数量是未定义的,因此递归解决方案似乎适合此任务。作为给定候选集的示例

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

应该得到 12 种组合:

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

候选集表示为列表类型的列表:List>

【问题讨论】:

    标签: java recursion


    【解决方案1】:

    几年前我也遇到过同样的问题。我通过使用里程表遍历结果列表解决了这个问题。

    里程表中的轮数是输入组的数量。每个轮子上的数字是相应组的成员。要获得下一个排列,滚动最右边的里程表轮。如果它一直转动,则将其滚动到左侧,等等。

    例如:

    Wheel 0 values: [1,2]
    Wheel 1 values: [3,4]
    Wheel 2 values: [5,6,7]
    

    从里程表读数开始 (1,3,5)。前进到(1,3,6),(1,3,7)。然后将下一个轮子也滚动到(1,4,5),(1,4,6)和(1,4,7)。继续。

    里程表轮作为指标

    或者,您可以将轮子表示为相应列表中的索引。

    Wheel 0 values: [0,1]
    Wheel 1 values: [0,1]
    Wheel 2 values: [0,1,2]
    

    从里程表读数 (0,0,0) 开始。前进到 (0,0,1), (0,0,2)。然后将下一个轮子也滚动到(0,1,0),(0,1,1)和(0,1,2)。继续。对于每个读数,通过使用里程表轮读数作为输入列表的索引来转换为结果列表。

    里程计轮作为迭代器

    作为另一种选择,您可以将轮子表示为输入集合中的迭代器。这比前两种方法更通用。即使索引无法访问输入集合,它也可以工作。而且它是可扩展的。这是我几年前使用的方法。

    【讨论】:

      【解决方案2】:

      组合总数是候选集大小的乘积。每个结果集的大小等于候选集的数量。

      解决方案不需要递归。只需遍历每个候选集。在这个例子中,第一个有两个值,1 和 2。前 6 个结果集(其中一半)将第一个值作为 1。下半个结果集得到 6。

      在下一个候选集上,有两个值,3 和 4。但这一次,将它们交替分配为 3 个一组,而不是 6 个。所以前 3 个结果集得到 3,接下来的 3 个结果集得到 4,接下来的 3 得到 3,以此类推。

      下一个候选集具有三个值:5、6 和 7。您现在将轮换为每个结果集分配的值(轮换每 1 个分配)。如果有更多候选集或不同数量的值在它们中,您在旋转到下一个值之前分配的数量会发生变化。但您可以通过编程方式解决此问题。

      【讨论】:

        【解决方案3】:

        你不需要递归。只需使用集合列表的大小,然后使用每个集合的大小。您可以让结果保持开放状态,以便添加更多元素,以防将来您需要混入更多集合。

        【讨论】:

          【解决方案4】:

          感谢大家的回复。

          Andy Thomas,里程表的想法很有趣。稍后会尝试一下。目前我已经按照 ThatOneCloud 的建议实现了它。

          这是我所拥有的(对于整数项目;如果需要可以概括):

          public List<List<Integer>> makeCombinations(List<List<Integer>> candidates) {
          
              List<List<Integer>> result = new ArrayList<List<Integer>>();
          
              // calculate result size
              int size = 1;
              for (List<Integer> candidateSet : candidates)
                  size *= candidateSet.size();
          
              // make result
              for (int i = 0; i < size; i++)
                  result.add(new ArrayList<Integer>());
          
              // fill result
              int pos = 1;
              for (List<Integer> candidateSet : candidates)
                  fillPosition(candidateSet, result, countRepeats(candidates, pos++));
          
              // return
              return result;
          }
          
          public int countRepeats(List<List<Integer>> candidates, int pos) {
          
              int repeats = 1;
              for (int i = pos; i < candidates.size(); i++)
                  repeats *= candidates.get(i).size();
              return repeats;
          }
          
          public void fillPosition(   List<Integer>      candidateSet, 
                                      List<List<Integer>>      result, 
                                      int                     repeats) {
          
              int idx = 0;
              while (idx < result.size()) {
                  for (int item : candidateSet) {
                      for (int i = 0; i < repeats; i++) {
                          result.get(idx++).add(item);
                      }
                  }
              }
          }
          

          【讨论】:

            【解决方案5】:

            这是另一个版本(里程表,正如 Andy Thomas 建议的那样)

            import java.util.ArrayList;
            import java.util.Iterator;
            import java.util.List;
            
            public class Odometer<T> implements Iterable<List<T>> {
            
                private class Wheel {
                    List<T> values;
                    int         idx = 0;
            
                    /**
                     * Create an odometer wheel from list of values
                     * @param v
                     */
                    protected Wheel (List<T> v) {
                        if (v == null)   throw new NullPointerException("can't create an instance of Wheel.class with null values");
                        if (v.isEmpty()) throw new IllegalArgumentException("can't create an instance of Wheel.class with no values");
                        this.values = v;
                    }
            
                    /**
                     * Get wheel value
                     * @return
                     */
                    protected T value() {
                        return values.get(idx);
                    }
            
                    /**
                     * switch an odometer wheel one step 
                     * @return TRUE - if a wheel have made full cycle and have switched to first item
                     */
                    protected boolean next() {
                        if (idx >= values.size() - 1) {
                            idx = 0; 
                            return true;
                        } else {
                            idx++;
                            return false;
                        }
            
                    }
                }
            
                /**
                 * list of wheels
                 */
                private List<Wheel> wheels = new ArrayList<Wheel>();
            
                /**
                 * Create an odometer from several lists of values 
                 * (each List<T> is a list of values for one odometer wheel)
                 * @param values
                 */
                public Odometer(List<List<T>> values) {
                    for (List<T> v : values)
                        wheels.add(new Wheel(v));
                }
            
                /**
                 * Get odometer value
                 * @return
                 */
                public List<T> get() {
                    List<T> result = new ArrayList<T>();
                    for (Wheel wheel : wheels) {
                        result.add(wheel.value());
                    }
                    return result;
                }
            
                /**
                 * Switch to next value
                 * @return TRUE if full cycle is finished
                 */
                public boolean next() {
                    for (int i = wheels.size() - 1; i >= 0; i--)
                        if (!wheels.get(i).next()) return false;
                    return true;
                }
            
                /**
                 * Reset odometer
                 */
                public void reset() {
                    for (Wheel wheel : wheels)
                        wheel.idx = 0;
                }
            
                /**
                 * Iterator
                 */
                @Override
                public Iterator<List<T>> iterator() {
                    reset();
                    Iterator<List<T>> it = new Iterator<List<T>>() {
                        private boolean last = false;
            
                        @Override
                        public boolean hasNext() {
                            return !last;
                        }
                        @Override
                        public List<T> next() {
                            List<T> result = get();
                            last = Odometer.this.next();
                            return result;
                        }
                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException();
                        }
                    };
                    return it;  
                }
            
                public static void main(String [] args) {
                    List<Integer> l1 = new ArrayList<Integer>(); l1.add(1); l1.add(2);
                    List<Integer> l2 = new ArrayList<Integer>(); l2.add(3); l2.add(4); l2.add(5);
                    List<Integer> l3 = new ArrayList<Integer>(); l3.add(6); l3.add(7);
                    List<List<Integer>> list = new ArrayList<List<Integer>>(); list.add(l1); list.add(l2); list.add(l3); 
                    Odometer<Integer> odometer = new Odometer<Integer>(list);
                    for (List<Integer> value : odometer) {
                        System.out.println(value);
                    }
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-02-04
              • 1970-01-01
              • 1970-01-01
              • 2014-02-28
              • 2022-01-20
              • 2014-12-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多