Java:将一组整数的所有可能组合放在矩阵列表中的算法

标签 java algorithm matrix combinations permutation

我正在尝试开发一个程序,给定一组节点和一组人都将整数作为键。

我需要找到所有可能的组合,让人们通过所有节点。一个人可以自己去所有的节点,也可以由不同的人分流。

由于人是同质的,所以两个人以相同的顺序通过相同的节点,因此只能算作一个解决方案。

例如 Sol: Person 0= [1], Person1= [2], and Person 2= [3] 等价于 Sol: Person 0 = [2], Person 1 = [1] and Person 2 = [3] 或 Sol: Person 0= [1], Person 1= [3, 2], Person 2= null 将等同于 Sol: , Person 1= [3, 2], Person 1= null ,Person 2= [1].

我使用整数矩阵 Sol 的列表来存储所有可能的路径 Integer[person][nodes]。所以我想将解决方案存储在集合或列表中。那将是 Set 或 List。

所以 Sol[0]= 将等于 0 号人经过的所有节点。

如果我们有 3 个人(0,1,2)和 3(1,2,3)个节点,所有可能的解决方案将是:

> Sol 1: 
Person 0= [1]
Person 1= [2]
Person 2= [3]


>Sol 2: 
Person 0= [3]
Person 1= [2, 1]
Person 2= null

>Sol 3: 
Person 0= [3]
Person 1= [1, 2]
Person 2= null

>Sol 4: 
Person 0= [1]
Person 1= [3, 2]
Person 2= null

>Sol 5: 
Person 0 = [1]
Person 1= [2, 3]
Person 2= null

>Sol 6: 
Person 0 = [2]
Person 1= [1, 3]
Person 2= null

>Sol 7: 
Person 0 = [2]
Person 1= [3, 1]
Person 2= null

>Sol 8: 
Person 0 = [3, 2, 1]
Person 1= null
Person 2= null

>Sol 9: 
Person 0 = [1, 2, 3]
Person 1= null
Person 2= null

>Sol 10: 
Person 0 = [3, 1, 2]
Person 1= null
Person 2= null

>Sol 11: 
Person 0 = [2, 1, 3]
Person 1= null
Person 2= null

>Sol 12: 
Person 0 = [1, 3, 2]
Person 1= null
Person 2= null

>Sol 13: 
Person 0 = [2, 3, 1]
Person 1= null
Person 2= null

面对这个问题,我最初的想法是开始使用所有节点添加人 0 的所有路径,从人 0 的路径中取出除 1 之外的所有节点并添加到人 1,然后获取所有从人 0 中除 1 之外的节点,并将它们添加到人 1.. 等等。

然后我将调用用于从 Person 0 和 Person 1 组合生成路径的相同函数,并为 Person 1(使用之前生成的路径)和 Person 2 调用它。因此它对于递归算法(在我的意见)。

当我有两个人时,我得到了所有可能的解决方案,但我对如何为无限数量的人和节点实现它感到困惑。

代码:

public static void function(List<Sol> solutions, int startingPerson, Integer[] people, List<Integer> Numbers, Sol part) {
    Set<List<Integer>> result;
    Set<List<Integer>> resResult;
    int i = 0;
    for (Integer j = Numbers.size(); j >= 0; j--) {

        if (j == 1 && Numbers.size() <= people.length) {

            for (int l = startingPerson; l < Numbers.size() + startingPerson; l++) {
                Integer[] in2 = new Integer[1];
                in2[0] = Numbers.get(l - startingPerson);
                part.getSol()[l] = in2;
            }

            Arrays.fill(part.getSol(), null);
            solutions.add(org.apache.commons.lang3.SerializationUtils.clone(part));
            Arrays.fill(part.getSol(), null);

        }


        /*We get all combinations (order not important) of a certain number of the nodes*/
        Combinations it = new Combinations(Numbers, i);
        Iterator<List<Integer>> s = it.iterator();
        Set<List<Integer>> l2 = new HashSet<>();
        while (s.hasNext()) {
            List<Integer> listares = s.next();
            l2.add(listares);
        }
                /*In l2 we have all the combination so loop them to add them to the paths*/
        for (List<Integer> l3 : l2) {
                    /*We get all possible permutations for the numbers of the combination */
            result = permute(l3);
                    /*We loop all possible permutations*/
            for (List<Integer> l4 : result) {
                int k = startingPerson;
                ArrayList<Integer> res = new ArrayList<>(Numbers);
                res.removeAll(l4);

                Integer[] array = new Integer[l4.size()];
                l4.toArray(array);

                //*We get all the permutations from res numbers*//
                resResult = permute(res);

                //*So we won't repeat same paths*//
                if (!res.isEmpty() && (res.size() <= Math.nextUp(Numbers.size() / 2))) {
                    for (List<Integer> l5 : resResult) {
                        Integer[] array2 = new Integer[l5.size()];
                        l5.toArray(array2);
                        part.getSol()[k] = array2;

                    }

                }

                /*Means that we are only using a person to go through all the nodes*/
                if (res.isEmpty()) {

                    part.getSol()[k] = array;
                    solutions.add(org.apache.commons.lang3.SerializationUtils.clone(part));
                    Arrays.fill(part.getSol(),null);

                /*More than one person to go through the nodes*/
                } else if (res.size() <= Math.nextUp(Numbers.size() / 2)) {

                    part.getSol()[k + 1] = array;
                    solutions.add(org.apache.commons.lang3.SerializationUtils.clone(part));
                    part.getSol()[k + 1] = null;
                    k++;
                        /*We only can take numbers from the combination if we have more than one element */
                        /*if(array.length>1) {
                            function(solutions, k, people, l4, part);
                        }*/


                }


            }


        }
        i++;
    }


}

    public static Set<List<Integer>> permute(List<Integer> ls) {
        // we use a Set of Sets rather than a list of arrays
        // because Sets support adding in the middle
        // and track current length
        Set<List<Integer>> permutations = new HashSet<>();
        // Add an empty Set so that the middle for loop runs
        permutations.add(new ArrayList<>());

        for (int i = 0; i < ls.size(); i++) {
            // create a temporary container to hold the new permutations
            // while we iterate over the old ones
            Set<List<Integer>> current = new HashSet<>();
            for (List<Integer> permutation : permutations) {
                for (int j = 0, n = permutation.size() + 1; j < n; j++) {
                    List<Integer> temp = new ArrayList<>(permutation);
                    temp.add(j, ls.get(i));
                    current.add(temp);
                }
            }
            permutations = new HashSet<>(current);
        }

        return permutations;
    }


public class Combinations implements Iterable<List<Integer>> {
    private List<Integer> lista;
    private Integer k;

    public Combinations(List<Integer> s, Integer k) {
        lista = s;
        this.k = k;
    }

    @Override
    public Iterator<List<Integer>> iterator() {

        return new IteratorCombn(lista, k);
    }

    private class IteratorCombn implements Iterator<List<Integer>> {
        private int actualSize, maxresult;
        private Integer curIndex;
        private Integer[] result;
        private int[] indices;
        private Integer[] arrayList;
        private List<Integer> elem = null;

        public IteratorCombn(List<Integer> s, Integer k) {
            actualSize = k;// desde donde
            curIndex = 0;
            maxresult = k;
            arrayList = new Integer[s.size()];
            for (int i = 0; i < arrayList.length; i++) {
                arrayList[i] = s.get(i);
            }
            this.result = new Integer[actualSize < s.size() ? actualSize : s.size()];

            indices = new int[result.length];

            for (int i = 0; i < result.length; i++) {
                indices[i] = result.length - 2 - i;
            }
        }

        public boolean hasNext() {
            elem = null;
            while ((elem == null && curIndex != -1)) {
                if(indices.length==0){
                    return false;
                }
                indices[curIndex]++;
                if (indices[curIndex] == (curIndex == 0 ? arrayList.length: indices[curIndex - 1])) {

                    indices[curIndex] = indices.length - curIndex - 2;
                    curIndex--;
                } else {

                    result[curIndex] = arrayList[indices[curIndex]];

                    if (curIndex < indices.length - 1)
                        curIndex++;
                    else {
                        elem = new ArrayList<>(result.length);

                        for (Integer s : result) {
                            elem.add(s);

                        }

                    }
                }
            }
            if (elem == null) {
                if (actualSize < maxresult) {
                    actualSize++;
                    this.result = new Integer[actualSize < arrayList.length ? actualSize
                            : arrayList.length];
                    indices = new int[result.length];

                    for (int i = 0; i < result.length; i++) {

                        indices[i] = result.length - 2 - i;
                    }
                    curIndex = 0;

                    return this.hasNext();
                } else {
                    return false;
                }
            } else {
                return true;
            }
        }

        @Override
        public List<Integer> next() {
            return elem;
        }

        @Override
        public void remove() {
            // TODO Auto-generated method stub

        }
    }
}

Sol 类是一个只包含 Integer[][] sol 的类

我只使用一个矩阵,并且一直在更改它,因为我想节省 JVM 内存。

我想知道是否有人可以帮助我解决问题,或者给我另一个解决问题的想法。

谢谢。

最佳答案

根据评论中我自己的建议,我决定在规范解决方案中,所有访问一个或多个节点的人都排在第一位,而没有访问任何节点的人排在最后。访问节点的人按他们访问的第一个节点排序。除此之外,我从头开始编写了自己的解决方案,而不是使用您的解决方案。通过在几个地方修剪搜索树,我的解决方案可以变得更有效率;但它确实打印了您在问题中给出的相同的 13 个解决方案。

public class Combine {

    public static final int nNodes = 3;
    public static final int nPersons = 3;

    // nodes
    private Node[] nodes;
    private int nUnvisitedNodes = nNodes;

    // solution
    private Node[][] sol = new Node[nPersons][];

    // no of solutions already found and printed
    int nSolutions = 0;

    public Combine() {
        // init nodes
        nodes = new Node[nNodes];
        for (int nix = 0; nix < nodes.length; nix++) {
            nodes[nix] = new Node(nix + 1); // node names are 1-based
        }

        // search for solutions -- person 0 first
        tryPerson0();
    }

    private void tryPerson0() {
        if (nUnvisitedNodes == 0) { // done
            printSolution();
        } else {
            // assuming this is not the last person, this person may visit 1 through nUnvisitedNodes nodes
            // (in a canonical solution person 0 cannot visit 0 nodes)
            int maxVisits = nUnvisitedNodes;
            for (int nNodesToVisit = 1; nNodesToVisit <= maxVisits; nNodesToVisit++) {
                sol[0] = new Node[nNodesToVisit];
                tryNode(0, sol[0], 0);
                sol[0] = null;
            }
        }
    }

    private void tryPerson(int person) {
        assert person > 0;
        if (nUnvisitedNodes == 0) { // solution complete
            printSolution();
        } else {
            if (person < nPersons) { // still persons to try
                if (person == nPersons - 1) { // this is the last person
                    // person must visit all remaining nodes
                    // in a canonical solution, first node must be greater than first node visited by previous person
                    int nNodesToVisit = nUnvisitedNodes;
                    sol[person] = new Node[nNodesToVisit];
                    tryNodeWithMininum(person, sol[person], 0, sol[person - 1][0].name + 1);
                    sol[person] = null;
                } else {
                    // since this is not the last person, this person may visit 1 through nUnvisitedNodes nodes
                    // in a canonical solution, first node must be greater than first node visited by previous person
                    int maxVisits = nUnvisitedNodes;
                    for (int nNodesToVisit = 1; nNodesToVisit <= maxVisits; nNodesToVisit++) {
                        sol[person] = new Node[nNodesToVisit];
                        tryNodeWithMininum(person, sol[person], 0, sol[person - 1][0].name + 1);
                        sol[person] = null;
                    }
                }
            }
        }
    }

    private void tryNode(int person, Node[] personSol, int nix) {
        if (nix == personSol.length) { // this person's solution complete
            tryPerson(person + 1);
        } else {
            for (Node candidateNode : nodes) {
                if (candidateNode.visit()) {
                    personSol[nix] = candidateNode;
                    tryNode(person, personSol, nix + 1);
                    personSol[nix] = null;
                    candidateNode.unvisit();
                }
            }
        }
    }

    private void tryNodeWithMininum(int person, Node[] personSol, int nix, int minNodeName) {
        for (Node candidateNode : nodes) {
            if (candidateNode.getName() >= minNodeName) {
                if (candidateNode.visit()) {
                    personSol[nix] = candidateNode;
                    tryNode(person, personSol, nix + 1);
                    personSol[nix] = null;
                    candidateNode.unvisit();
                }
            }
        }
    }

    private void printSolution() {
        nSolutions++;
        System.out.println("> Sol " + nSolutions);
        for (int pix = 0; pix < nodes.length; pix++) {
            System.out.println("Person " + pix + " = " + Arrays.toString(sol[pix]));
        }
        System.out.println();
    }

    public static void main(String[] args) {
        new Combine();
    }

    private class Node {
        int name;
        boolean visited = false;

        public Node(int name) {
            this.name = name;
        }

        /** visits node if not already visited */
        public boolean visit() {
            if (visited) {
                return false;
            } else {
                visited = true;
                nUnvisitedNodes--;
                return true;
            }
        }

        /** undoes visit (that is, backtracks) */
        public void unvisit() {
            assert visited : name;
            nUnvisitedNodes++;
            visited = false;
        }

        public int getName() {
            return name;
        }

        @Override
        public String toString() {
            return String.valueOf(name);
        }
    }

}

关于Java:将一组整数的所有可能组合放在矩阵列表中的算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37634380/

相关文章:

java - 使网络密集型代码更加健壮

algorithm - 渲染不透明、启用 Alpha 和启用混合的对象

algorithm - 如何对玩家进行分组?

按顺序计算下一组的算法

swift - 这个矩阵相邻单元格计数的解决方案有什么问题? ( swift )

matlab - 求矩阵元素匹配条件的索引 - Matlab

matlab - 如何在Matlab中使用系统矩阵获得多输入多输出系统的传递函数

java - 字符串不变性允许缓存哈希码值

java - 字符串中字符的位置和重复

java - 仅使用 volatile 检查并行完成