[英]Split a list into two sublists in all possible ways
我有一個可變大小的列表,例如
[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, [])
但是,它使用庫功能,一般看起來不是很好。
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++);
}
}
使用按位算術計算應易於轉換為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)
雖然在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
。 那是為了避免重復。
遍歷所有不同大小的組合並從原始列表中“減去”它們似乎是直觀的方法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)
OUTPUT
[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;
}
OUTPUT
[1] [2, 3]
[2] [1, 3]
[3] [1, 2]
[2, 3] [1]
[1, 2] [3]
[1, 3] [2]
[1, 2, 3] []
我發現其他解決方案很難理解,這些問題可以通過遞歸輕松解決:
alist = [1, 2, 3, 4]
def partition(alist, left, right, index):
if index >= len(alist):
print(left, right)
return
partition(alist, left + [alist[index]], right, index + 1)
partition(alist, left, right + [alist[index]], index + 1)
print(partition(alist, [], [], 0))
或使用生成器:
def partition(alist, left, right, index):
if index >= len(alist):
yield left, right
else:
yield from partition(alist, left + [alist[index]], right, index + 1)
yield from partition(alist, left, right + [alist[index]], index + 1)
print(list(partition(alist, [], [], 0)))
Output:
[1, 2, 3, 4] []
[1, 2, 3] [4]
[1, 2, 4] [3]
[1, 2] [3, 4]
[1, 3, 4] [2]
[1, 3] [2, 4]
[1, 4] [2, 3]
[1] [2, 3, 4]
[2, 3, 4] [1]
[2, 3] [1, 4]
[2, 4] [1, 3]
[2] [1, 3, 4]
[3, 4] [1, 2]
[3] [1, 2, 4]
[4] [1, 2, 3]
[] [1, 2, 3, 4]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.