Java 获取集合中所有可能的分区
在Java中,我有一个集合,我想获得所有可能的子集组合,它们的并集构成了主集合。(对集合进行分区) 例如,假设:Java 获取集合中所有可能的分区,java,collections,set,partitioning,Java,Collections,Set,Partitioning,在Java中,我有一个集合,我想获得所有可能的子集组合,它们的并集构成了主集合。(对集合进行分区) 例如,假设: set={1,2,3} 结果应该是: { {{1,2,3}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}} 一组n元素的可能分区数为B(n)称为 迄今为止的守则: public static <T> Set<Set<T>> powerSet(Set<T> myse
set={1,2,3}
结果应该是:
{ {{1,2,3}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}}
一组n
元素的可能分区数为B(n)
称为
迄今为止的守则:
public static <T> Set<Set<T>> powerSet(Set<T> myset) {
Set<Set<T>> pset = new HashSet<Set<T>>();
if (myset.isEmpty()) {
pset.add(new HashSet<T>());
return pset;
}
List<T> list = new ArrayList<T>(myset);
T head = list.get(0);
Set<T> rest = new HashSet<T>(list.subList(1, list.size()));
for (Set<T> set : powerSet(rest)) {
Set<T> newSet = new HashSet<T>();
newSet.add(head);
newSet.addAll(set);
pset.add(newSet);
pset.add(set);
}
return pset;
}
最简单的解决方案是对集合中的元素数使用递归:构建一个函数来构造一个n元素集合的所有分区。对于n+1个元素,您可以将新元素添加到现有分区集中的一个分区集中,或者将其放入自己的分区集中。我们将把该分区集中的元素转换为数组并使用列表,以便能够保留索引。一旦我们接收到集合的所有数学子集,我们将转换回Java集合 此外,我们将使用T[]模板,因为您已经对集合使用了泛型。我们需要这个数组来定义转换为数组的类型
public <T>Set<Set<T>> subsets(Set<T> nums,T[] template) {
T[] array = nums.toArray(template);
List<List<T>> list = new ArrayList<>();
subsetsHelper(list, new ArrayList<T>(), array, 0);
return list.stream()
.map(t->t.stream().collect(Collectors.toSet()))
.collect(Collectors.toSet());
}
private <T>void subsetsHelper(List<List<T>> list , List<T> resultList, T[] nums, int start){
list.add(new ArrayList<>(resultList));
for(int i = start; i < nums.length; i++){
// add element
resultList.add(nums[i]);
// Explore
subsetsHelper(list, resultList, nums, i + 1);
// remove
resultList.remove(resultList.size() - 1);
}
}
公共集合子集(集合nums,T[]模板){
T[]数组=nums.toArray(模板);
列表=新的ArrayList();
SubsetHelper(列表,新ArrayList(),数组,0);
return list.stream()
.map(t->t.stream().collect(Collectors.toSet()))
.collect(收集器.toSet());
}
私有void subsetHelper(列表列表、列表结果列表、T[]nums、int start){
添加(新的ArrayList(resultList));
对于(int i=开始;i
源代码是这里介绍的算法的修改版本,以满足泛型要求
搜索算法的解决方案是: 在伪代码中:
Set<T> base; //the base set
Set<Set<T>> pow; //the power set
Set<Set<Set<T>>> parts; //the partitions set
function findAllPartSets():
pow = power set of base
if (pow.length > 1) {
pow.remove(empty set);
}
for p in pow:
findPartSets(p);
function findPartSets(Set<Set<T>> current):
maxLen = base.length - summed length of all sets in current;
if (maxLen == 0) {
parts.add(current);
return;
}
else {
for i in 1 to maxLen {
for s in pow {
if (s.length == i && !(any of s in current)) {
Set<Set<T>> s2 = new Set(current, s);
findPartSets(s2);
}
}
}
}
创建的输出与您要求的预期输出类似,只是任何解决方案中都没有空集(输入集为空时除外)。
因此,生成的集合分区和集合分区数现在符合。首先,powerset:
对于n个不同的元素,您可以创建2n个集合,当考虑“此元素是否包含在此特定集合中?”这一布尔值时,可以很容易地显示这些集合:
对于n=3:
因此,可以通过迭代0到2的整数来实现对所有组合的迭代ⁿ 并使用每个数字的位模式从原始集合中选择元素(为此,我们必须将它们复制到一个有序结构中,如列表
):
为了利用它来标识分区,我们必须扩展逻辑,使用计数器的位从另一个掩码中选择位,该掩码将标识要包含的实际元素。然后,我们可以使用一个简单的二进制not操作,重新使用相同的操作来获取到目前为止未包含的元素的分区:
public static <T> Set<Set<Set<T>>> allPartitions(Set<T> input) {
List<T> sequence = new ArrayList<>(input);
if(sequence.size() > 62) throw new OutOfMemoryError();
return allPartitions(sequence, (1L << sequence.size()) - 1);
}
private static <T> Set<Set<Set<T>>> allPartitions(List<T> input, long bits) {
long count = 1L << Long.bitCount(bits);
if(count == 1) {
return Collections.singleton(new HashSet<>());
}
Set<Set<Set<T>>> result = new HashSet<>();
for(long l = 1; l >= 0 && l < count; l++) {
long select = selectBits(l, bits);
final Set<T> first = get(input, select);
for(Set<Set<T>> all: allPartitions(input, bits&~select)) {
all.add(first);
result.add(all);
}
}
return result;
}
private static long selectBits(long selected, long mask) {
long result = 0;
for(long bit; selected != 0; selected >>>= 1, mask -= bit) {
bit = Long.lowestOneBit(mask);
if((selected & 1) != 0) result |= bit;
}
return result;
}
private static <T> Set<T> get(List<T> elements, long bits) {
if(bits == 0) return Collections.emptySet();
else if(Long.lowestOneBit(bits) == bits)
return Collections.singleton(elements.get(Long.numberOfTrailingZeros(bits)));
else {
HashSet<T> next = new HashSet<>();
for(; bits != 0; bits-=Long.lowestOneBit(bits)) {
next.add(elements.get(Long.numberOfTrailingZeros(bits)));
}
return next;
}
}
鉴于
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3", "4");
for(Set<Set<String>> partition: allPartitions(input))
System.out.println(partition);
你已经生成的代码是什么?我可以提取set的powerset,也可以提取两个set的分区,例如{{1,2},{3}和{1,3},{2}好的,编辑你的帖子,给我们可能重复的源代码,你能解释一下吗?你建立了一个函数,它以基集合的元素数n为参数。如果n=0,此函数只返回空集。否则,它用参数n-1调用自己,并使用上面解释的结果来计算n的答案。很抱歉,我不能提供代码片段,因为我不是Java程序员。但是这种递归方法是一种通用模式,在Java中也可以使用。它可以查找集合(powerset)的所有子集。答案是要求所有可能的分区。我认为算法不包括
[]
“更正确”,因为分区的定义是“集合X的分区是X的非空子集的集合,因此X中的每个元素X正好位于这些子集中的一个”。在这种情况下,您只需在构建分区之前从电源集中删除空集即可。我会编辑的。谢谢你的建议。我用jUnit测试了你的代码。它起作用了。我只做一个更改:唯一允许使用空集的情况应该是当输入集为空时,因此返回的分区数为1,使其符合。现在,它应该符合钟号。再次感谢您的建议:)如果我希望分区深度为x
或小于x
,我可以从电源集中删除大小大于x
的集合吗?
BaseSet: []
Result: [[[]]]
Base-Size: 0 Result-Size: 1
BaseSet: [1]
Result: [[[1]]]
Base-Size: 1 Result-Size: 1
BaseSet: [1, 2]
Result: [[[1], [2]], [[1, 2]]]
Base-Size: 2 Result-Size: 2
BaseSet: [1, 2, 3]
Result: [[[1], [2], [3]], [[1], [2, 3]], [[2], [1, 3]], [[1, 2], [3]], [[1, 2, 3]]]
Base-Size: 3 Result-Size: 5
BaseSet: [1, 2, 3, 4]
Result: [[[1], [2], [3], [4]], [[1], [2], [3, 4]], [[4], [1, 2, 3]], [[1], [3], [2, 4]], [[1, 2, 3, 4]], [[1], [4], [2, 3]], [[1], [2, 3, 4]], [[2], [3], [1, 4]], [[2], [4], [1, 3]], [[2], [1, 3, 4]], [[1, 3], [2, 4]], [[1, 2], [3], [4]], [[1, 2], [3, 4]], [[3], [1, 2, 4]], [[1, 4], [2, 3]]]
Base-Size: 4 Result-Size: 15
0: 0 0 0 none included
1: 0 0 1 first included
2: 0 1 0 second included
3: 0 1 1 first and second one included
4: 1 0 0 third included
5: 1 0 1 first and third included
6: 1 1 0 second and third included
7: 1 1 1 all included
public static <T> Set<Set<T>> allPermutations(Set<T> input) {
List<T> sequence = new ArrayList<>(input);
long count = sequence.size() > 62? Long.MAX_VALUE: 1L << sequence.size();
HashSet<Set<T>> result = new HashSet<>((int)Math.min(Integer.MAX_VALUE, count));
for(long l = 0; l >= 0 && l < count; l++) {
if(l == 0) result.add(Collections.emptySet());
else if(Long.lowestOneBit(l) == l)
result.add(Collections.singleton(sequence.get(Long.numberOfTrailingZeros(l))));
else {
HashSet<T> next = new HashSet<>((int)(Long.bitCount(l)*1.5f));
for(long tmp = l; tmp != 0; tmp-=Long.lowestOneBit(tmp)) {
next.add(sequence.get(Long.numberOfTrailingZeros(tmp)));
}
result.add(next);
}
}
return result;
}
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3");
System.out.println(allPermutations(input));
public static <T> Set<Set<Set<T>>> allPartitions(Set<T> input) {
List<T> sequence = new ArrayList<>(input);
if(sequence.size() > 62) throw new OutOfMemoryError();
return allPartitions(sequence, (1L << sequence.size()) - 1);
}
private static <T> Set<Set<Set<T>>> allPartitions(List<T> input, long bits) {
long count = 1L << Long.bitCount(bits);
if(count == 1) {
return Collections.singleton(new HashSet<>());
}
Set<Set<Set<T>>> result = new HashSet<>();
for(long l = 1; l >= 0 && l < count; l++) {
long select = selectBits(l, bits);
final Set<T> first = get(input, select);
for(Set<Set<T>> all: allPartitions(input, bits&~select)) {
all.add(first);
result.add(all);
}
}
return result;
}
private static long selectBits(long selected, long mask) {
long result = 0;
for(long bit; selected != 0; selected >>>= 1, mask -= bit) {
bit = Long.lowestOneBit(mask);
if((selected & 1) != 0) result |= bit;
}
return result;
}
private static <T> Set<T> get(List<T> elements, long bits) {
if(bits == 0) return Collections.emptySet();
else if(Long.lowestOneBit(bits) == bits)
return Collections.singleton(elements.get(Long.numberOfTrailingZeros(bits)));
else {
HashSet<T> next = new HashSet<>();
for(; bits != 0; bits-=Long.lowestOneBit(bits)) {
next.add(elements.get(Long.numberOfTrailingZeros(bits)));
}
return next;
}
}
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3");
System.out.println(allPartitions(input));
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3", "4");
for(Set<Set<String>> partition: allPartitions(input))
System.out.println(partition);