【问题标题】:Split a list into two sublists in all possible ways以所有可能的方式将列表拆分为两个子列表
【发布时间】:2015-06-21 19:01:58
【问题描述】:

我有一个可变大小的列表,例如

[1, 2, 3, 4]

我想尽一切可能将这个列表一分为二:

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

我很确定这不是一个未知问题,并且可能有一种算法可以解决这个问题,但是我找不到。此外,这不应使用任何外部库,而应使用大多数语言中的简单语言特性(循环、条件、方法/函数、变量...)。

我用 Python 编写了一个 hackish 解决方案:

def get_all(objects):
    for i in range(1, len(objects)):
        for a in combinations(objects, i):
            for b in combinations([obj for obj in objects if obj not in up], len(objects) - i):
                yield State(up, down)
    if objects:
        yield State([], objects)
        yield State(objects, [])

但是,它使用库功能,总体上看起来不是很好。

【问题讨论】:

  • 我们这里不写代码。我们帮助人们找到他们的解决方案。您需要为此付出一些努力。
  • 我用 Python 编写了一个 hackish 解决方案。
  • @Leo 然后将其发布在您的问题中。并提出具体问题,关于您的解决方案有什么问题。
  • 订单是否相关?
  • ([a,b], []) = ([b, a], []) != ([], [a, b])

标签: java python algorithm list sublist


【解决方案1】:
l = [1, 2, 3, 4]
flags = [False] * len(l)
while True:
    a = [l[i] for i, flag in enumerate(flags) if flag]
    b = [l[i] for i, flag in enumerate(flags) if not flag]
    print a, b
    for i in xrange(len(l)):
        flags[i] = not flags[i]
        if flags[i]:
            break
    else:
        break

结果:

[] [1, 2, 3, 4]
[1] [2, 3, 4]
[2] [1, 3, 4]
[1, 2] [3, 4]
[3] [1, 2, 4]
[1, 3] [2, 4]
[2, 3] [1, 4]
[1, 2, 3] [4]
[4] [1, 2, 3]
[1, 4] [2, 3]
[2, 4] [1, 3]
[1, 2, 4] [3]
[3, 4] [1, 2]
[1, 3, 4] [2]
[2, 3, 4] [1]
[1, 2, 3, 4] []

可以轻松适配java:

public static void main(String[] args) {
    int[] l = new int[] { 1, 2, 3, 4 };
    boolean[] flags = new boolean[l.length];
    for (int i = 0; i != l.length;) {
        ArrayList<Integer> a = new ArrayList<>(), b = new ArrayList<>();
        for (int j = 0; j < l.length; j++)
            if (flags[j]) a.add(l[j]); else b.add(l[j]);
        System.out.println("" + a + ", " + b);
        for (i = 0; i < l.length && !(flags[i] = !flags[i]); i++);
    }
}

【讨论】:

  • 这实际上可能是一种可能性,因为像产品这样的东西很容易实现/存在于大多数主要语言中。我不确定 zip,但查看 python 文档,它看起来也是用其他语言开发的(相对)简单的东西。
  • 在 Python 的答案中观察到 for-else 构造的微妙之处。你可以在这里查看stackoverflow.com/questions/9979970/…
【解决方案2】:

查看所有不同大小的组合并从原始列表中“减去”它们似乎是 IMO 直观的方法:

from itertools import combinations

s = [1, 2, 3, 4]
for combs in (combinations(s, r) for r in range(len(s)+1))  :
    for comb in combs:
        diff = list(set(s[:]) - set(comb))
        print diff, list(comb)

输出

[1, 2, 3, 4] []
[2, 3, 4] [1]
[1, 3, 4] [2]
[1, 2, 4] [3]
[1, 2, 3] [4]
[3, 4] [1, 2]
[2, 4] [1, 3]
[2, 3] [1, 4]
[1, 4] [2, 3]
[1, 3] [2, 4]
[1, 2] [3, 4]
[4] [1, 2, 3]
[3] [1, 2, 4]
[2] [1, 3, 4]
[1] [2, 3, 4]
[] [1, 2, 3, 4]

同样的方法也可以用在 Java 上(只是它更冗长……):

private static List<Integer> initial;

public static void main(String[] args) throws IOException {
    initial = Arrays.asList(1, 2, 3);
    combinations(initial);
}

static void combinations(List<Integer> src) {
    combinations(new LinkedList<>(), src);
}

private static void combinations(LinkedList<Integer> prefix, List<Integer> src) {        
    if (src.size() > 0) {
        prefix = new LinkedList<>(prefix); //create a copy to not modify the orig
        src = new LinkedList<>(src); //copy
        Integer curr = src.remove(0);
        print(prefix, curr); // <-- this is the only thing that shouldn't appear in a "normal" combinations method, and which makes it print the list-pairs
        combinations(prefix, src); // recurse without curr
        prefix.add(curr);
        combinations(prefix, src); // recurse with curr
    }
}

// print the prefix+curr, as one list, and initial-(prefix+curr) as a second list
private static void print(LinkedList<Integer> prefix, Integer curr) {
    prefix = new LinkedList<>(prefix); //copy
    prefix.add(curr);
    System.out.println(Arrays.toString(prefix.toArray()) +
                    " " + Arrays.toString(subtract(initial, prefix).toArray()));
}

private static List<Integer> subtract(List<Integer> initial, LinkedList<Integer> prefix) {
    initial = new LinkedList<>(initial); //copy
    initial.removeAll(prefix);
    return initial;
}

输出

[1] [2, 3]
[2] [1, 3]
[3] [1, 2]
[2, 3] [1]
[1, 2] [3]
[1, 3] [2]
[1, 2, 3] []

【讨论】:

  • 如果我有很棒的 Python 库可用,这是完美的,但在 Java 和 C 等语言中很难编写。
  • @LeoTietz 您想要一个不需要库的答案,请查看答案的 Java 部分。
【解决方案3】:

虽然在 Python 中,通过其丰富的库很容易获得结果,但在 Java 中,您可以编写递归解决方案。以下将打印您的数组的所有可能组合:

public static void main(String[] args) {
    List<Integer> num = Arrays.asList(1, 2, 3, 4);
    List<List<Integer>> sublists = new ArrayList<List<Integer>>();
    for (int i = 0; i <= num.size(); i++) {
      permutation(num, sublists, i, new ArrayList<Integer>(), 0);
    }

    for (List<Integer> subList : sublists) {
        List<Integer> numCopy = new ArrayList<Integer>(num);
        numCopy.removeAll(subList);
        System.out.println("(" + subList + ", " + numCopy + ")");
    }
}

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

sublists 包含迄今为止发现的所有组合。最后一个参数是当前子列表下一个元素的startIndex。那是为了避免重复。

【讨论】:

    【解决方案4】:

    一个更底层的解决方案,使用按位算术来计算应该易于转换为 Java 的子集:

    def sublists(xs):
        l = len(xs)
        for i in range(1 << l):
            incl, excl = [], []
            for j in range(l):
                if i & (1 << j):
                    incl.append(xs[j])
                else:
                    excl.append(xs[j])
            yield (incl, excl)
    

    【讨论】:

    • 这是一些非常聪明的东西,使用 2 的幂和二进制。 +(2^0)!
    猜你喜欢
    • 1970-01-01
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 2011-05-26
    相关资源
    最近更新 更多