I have a list of variable size, for example
[1, 2, 3, 4]
and I want to get every possible way to split this list into two:
([], [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], [])
I'm pretty sure this is not an unknown problem and there is probably an algorithm for this, however I could not find one. Also, this should not use any external libraries but work with simple language features (loops, conditions, methods/functions, variables, ...) found in most languages.
I've written a hackish solution in Python:
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, [])
However, it uses library features and is not very nice looking in general.
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
Result:
[] [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] []
It can easily be adapted to 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++);
}
}
A more low-level solution using bitwise arithmetic to count subsets that should be easy to translate to 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)
Though in Python, it's quite easy to get the result with its extensive library, in Java, you can write a recursive solution. The following will print all possible combinations of your array:
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);
}
}
}
The sublists
carries all the combinations found till now. The last parameter is the startIndex
for the next element of current sublist. That is to avoid duplicates.
Going over all the different sizes of combinations and "subtracting" them from the original list seems intuitive approach 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]
The same approach can be applied with Java (only that it's more verbose...):
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] []
来源:https://stackoverflow.com/questions/29656649/split-a-list-into-two-sublists-in-all-possible-ways