简体   繁体   English

以所有可能的方式将列表拆分为 n 个子列表

[英]Split a list into n sublists in all possible ways

I want to split a list into a given number n sublists in all possible ways in Java.我想在 Java 中以所有可能的方式将列表拆分为给定数量的 n 个子列表。

For example [1, 2, 3, 4] where n = 3 would include the following lists (but would not be a complete solution - complete would require much more space):例如[1, 2, 3, 4]其中 n = 3 将包括以下列表(但不是完整的解决方案 - 完整将需要更多空间):

([], [], [1,2,3,4])
([],[1],[2,3,4])
([],[1,2],[3,4])
([],[1,2,3],[4])
([],[1,2,3,4],[])
([1],[2,3,4], [])
([1],[2,3],[4])
([2,3],[4],[1])
([4],[],[1,2,3])
...

etc ETC

I adapted a solution from another similar question ( Split a list into two sublists in all possible ways ) however it only works for creating lists of 2 sublists and I am struggling to grasp how to implement it for a flexible rather than hardcoded number of sublists.我改编了另一个类似问题的解决方案( 以所有可能的方式将列表拆分为两个子列表)但是它仅适用于创建 2 个子列表的列表,我正在努力掌握如何实现它以实现灵活而不是硬编码的子列表数量。

Here is my code:这是我的代码:

public List<List<EGroup>> permutation(List<E> list) {
        List<List<E>> sublists = new ArrayList<List<E>>();
        for (int i = 0; i <= list.size(); i++) {
          permutationSplit(list, sublists, i, new ArrayList<E>(), 0);
        }

        List<List<EGroup>> listOfEGroupPairs = new ArrayList<List<EGroup>>();
       
        for (List<E> subList : sublists) {
            List<E> listCopy = new ArrayList<E>(list);
            listCopy.removeAll(subList);
            EGroup e1 = new EGroup(subList);
            EGroup e2 = new EGroup(listCopy);   
            List<EGroup> egr = new ArrayList<EGroup>();
            egr.add(e1);
            egr.add(e2);
            listOfEGroupPairs.add(egr);
        }
        
        return listOfEGroupPairs;
    }

   
    public void permutationSplit(List<E> list, List<List<E>> subLists, int sublistSize, List<E> currentSubList,
          int startIndex) {
        if (sublistSize == 0) {
          subLists.add(currentSubList);
        } else {
          sublistSize--;
          for (int i = startIndex; i < list.size(); i++) {
            List<E> newSubList = new ArrayList<E>(currentSubList);
            newSubList.add(list.get(i));
            permutationSplit(list, subLists, sublistSize, newSubList, i + 1);
          }
        }
    }

I need to create n number of EGroup objects to add to listOfEGroupPairs rather than the hardcoded 2, but how to always get the right number (n) of sublists of varied size each loop?我需要创建 n 个 EGroup 对象以添加到listOfEGroupPairs而不是硬编码的 2,但是如何始终在每个循环中获得正确数量 (n) 的不同大小的子列表?

You have K elements and each might fall into any of N lists.您有K个元素,每个元素都可能属于N个列表中的任何一个。 So there are N^K variants and we can just map integer values from 0 to N^K-1 to distributions like N-ary numeral system.所以有N^K变体,我们可以只 map integer 值从0N^K-1到像 N 进制数字系统这样的分布。

Another approach - recursively insert every element into N lists.另一种方法 - 递归地将每个元素插入到 N 个列表中。

I can demonstrate approaches with Python code recursive , N-ary and hope it might be translated to Java我可以使用 Python 代码递归N-ary来演示方法,并希望它可以转换为 Java

def recdistr(K, N, level, ls):
    if level == K:
        print(ls)
    else:
        for i in range(N):
            ls[i].append(level)
            recdistr(K, N, level + 1, ls)   #recursive call with changed list
            ls[i].pop()  #remove last added element to revert list to previous state

K = 4
N = 3
lst = [[] for _ in range(N)]
recdistr(K, N, 0, lst)

def mapdistr(K, N):
    for x in range(N**K):
        t = x
        l = [[] for _ in range(N)]
        for i in range(K):
            id = t % N
            t = t // N   #integer division
            l[id].append(i)
        print(l)

 mapdistr(K, N)

If I understand the question correctly, each element of the original list can end up in any of the n sublists.如果我正确理解了这个问题,原始列表的每个元素都可以在n个子列表中的任何一个中结束。 That means, there are n^s possible sublists ( s being the number of elements in the original list), which can be enumerated in a simple loop.这意味着,有n^s可能的子列表( s是原始列表中的元素数),可以在一个简单的循环中枚举。 With a bit of modulo and integer division you can then get the proper "bucket" for each element and prepare the results accordingly.通过一些模数和 integer 除法,您可以获得每个元素的正确“桶”并相应地准备结果。

public <T> List<List<List<T>>> partition(List<T> lst, int n) {
    var result = new ArrayList<List<List<T>>>();
    // k = SUM ( pos of lst[i] * n^i ) 
    for (int k = 0; k < Math.pow(n, lst.size()); k++) {
        // initialize result
        List<List<T>> res = IntStream.range(0, n)
                .mapToObj(i -> new ArrayList<T>())
                .collect(Collectors.toList());
        // distribute elements to sub-lists
        int k2 = k;
        for (int i = 0; i < lst.size(); i++) {
            res.get(k2 % n).add(lst.get(i));
            k2 /= n;
        }
        result.add(res);
    }
    return result;
}

Use recursion.使用递归。

For n equal to 0 or negative the task is impossible.对于n等于 0 或负数,该任务是不可能的。 Either throw an exception or return an empty lists of lists of sublists.抛出异常或返回子列表列表的空列表。 Corner case: if n is 0 and the list is empty, you may argue that the empty list of sublists is a valid response.极端情况:如果n为 0 并且列表为空,您可能会争辩说子列表的空列表是有效响应。

If n is 1, the only solution is trivially the entire list.如果n为 1,唯一的解决方案就是整个列表。

For n > 1 :对于n > 1

If the length of the list is 4 (for example [1, 2, 3, 4] ), there are 5 possible first lists.如果列表的长度为 4(例如[1, 2, 3, 4] ),则有 5 个可能的第一个列表。 In general there are list.length + 1 possible first sublists.通常有list.length + 1可能的第一个子列表。 Find them.找到他们。 For each such sublist make a recursive call passing the remainder of the list and n - 1 as arguments to find all possible combinations of sublists made from the remainder of the list.对于每个这样的子列表,进行递归调用,将列表的其余部分和n - 1作为 arguments 以查找从列表的其余部分组成的子列表的所有可能组合。 Combine each first sublist with each combination of remaining sublists to form a full solution.将每个第一个子列表与剩余子列表的每个组合结合起来,形成一个完整的解决方案。

Happy coding.快乐编码。

PS The solution as sketched will only produce sublists in the order they come in the original list. PS 草图中的解决方案只会按照它们在原始列表中的顺序生成子列表。 So the solution will include ([],[1],[2,3,4]) and ([1],[2,3,4], []) , but not ([4],[],[1,2,3]) .所以解决方案将包括([],[1],[2,3,4])([1],[2,3,4], []) ,但不包括([4],[],[1,2,3]) To regard the last one as a separate solution, you will additionally need to find all permutations of each solution, in turn taking into account that some sublists may be equal and hence swapping them won't make a distinct solution.要将最后一个视为单独的解决方案,您还需要找到每个解决方案的所有排列,进而考虑到某些子列表可能相等,因此交换它们不会产生不同的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM