如何递归获取Java中的所有组合?

时间:2015-10-20 14:04:24

标签: java recursion

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] 

候选人集合表示为类型列表列表:列表<列表< T>>

5 个答案:

答案 0 :(得分:2)

几年前我遇到过同样的问题。我通过使用里程表迭代结果列表来解决它。

里程表中的车轮数是输入组的数量。每个车轮上的数字是相应组的成员。为了得到下一个排列,滚动最右边的里程表轮。如果它一直转过来,将它滚到它左边等等。

例如:

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)。继续。对于每个读数,通过使用里程表轮读数作为输入列表的索引,转换为结果列表。

里程表车轮作为迭代器

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

下一个候选集有三个值:5,6和7.您将旋转为每个结果集分配的值(旋转每个1个赋值。)如果有更多候选集或不同数量对于它们中的值,在旋转到下一个值之前分配的数量将会改变。但你可以用编程方式解决这个问题。

答案 3 :(得分:0)

谢谢大家的回复。

安迪托马斯,与里程表相当有趣的想法。稍后会尝试一下。现在我已经按照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);
            }
        }
    }
}

答案 4 :(得分:0)

这是另一个版本(里程表,正如安迪托马斯建议的那样)

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