给定一个对象数组,当数组中的值可能重复时,我需要尽可能高效地找到给定数组的所有不同子集集,其中包括所有值。
例如:如果数组为1, 2, 1, 2
,那么我需要创建以下多重集:
{[1], [1], [2], [2]}
{[1], [1], [2, 2]}
{[1], [2], [1, 2]}
{[1], [1, 2, 2]}
{[1, 1], [2], [2]}
{[1, 1], [2, 2]}
{[1, 2], [1, 2]}
{[1, 1, 2], [2]}
{[1, 1, 2, 2]}
请注意,子集内的值顺序和多重集内的子集顺序都不重要。像{[1, 2, 2], [1]}
这样的多集与#4
相同,而{[2, 1], [2], [1]}
与#3
相同。
这里的示例是int,但实际上,我将不得不使用对象。
这应该尽可能高效。最好的方法是仅计算正确的(重复的)多集,而无需检查是否已经出现,因为创建它的方法将消除重叠现象。
我知道如何使用二进制表示形式创建所有子集。我将其与递归相结合来计算所有多重集。效果很好,只是当值重复时不起作用。这是我到目前为止所做的:
( a 是给定数字的数组, curr 是当前正在构建的多集, 并且 b 是所有多集的最终集合。)
public static void makeAll(ArrayList<Integer> a,
ArrayList<ArrayList<Integer>> curr,
ArrayList<ArrayList<ArrayList<Integer>>> b) {
ArrayList<ArrayList<Integer>> currCopy;
ArrayList<Integer> thisGroup, restGroup;
int currSize = 0, ii = 0;
if (a.size() == 0)
b.add(new ArrayList<ArrayList<Integer>>(curr));
else {
for (int i = 0; i < 1 << (a.size() - 1); i++) {
thisGroup = new ArrayList<>();
restGroup = new ArrayList<>();
ii = (i << 1) + 1; // the first one is always in, keeps uniquness.
for (int j = 0; j < a.size(); j++)
if ((ii & 1 << j) > 0)
thisGroup.add(a.get(j));
else
restGroup.add(a.get(j));
currSize = curr.size();
curr.add(new ArrayList<Integer>(thisGroup));
makeAll(restGroup, curr, b);
curr.subList(currSize, curr.size()).clear();
}
}
}
谢谢!
答案 0 :(得分:0)
这是“功率集”问题,具有两个以上的常用集合(通常包含在子集中或从子集中排除,因此功率集具有2 ^ N个元素)。对于您这个问题的版本,任何元素都可以是N个子集中的任何一个的一部分,因此问题的范围将扩展为N ^ N(很快就会变大)。
要找到给定N个元素列表的所有唯一分区,请从概念上生成以N为底的所有N位数字,然后数字的每个数字的值输入中相应元素的分区索引(这意味着通常,在所有情况下,您最终都会得到空分区,除非分区数等于N)。使用这些数字索引将元素分组为共享相同索引的元素列表,从而生成列表列表。为了检测重复项,您必须对子列表进行排序,然后对列表列表进行排序,然后将排序后的列表列表添加到集合中。您无法避免最后的重复数据删除步骤,因为您的描述允许输入中包含重复的元素。
package main;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class PrintPartitionings {
/** A list of integers */
public static class Partition extends ArrayList<Integer>
implements Comparable<Partition> {
// Lexicographic comparator
@Override
public int compareTo(Partition other) {
for (int i = 0, ii = Math.min(this.size(),
other.size()); i < ii; i++) {
int c = this.get(i).compareTo(other.get(i));
if (c != 0) {
return c;
}
}
return Integer.compare(this.size(), other.size());
}
}
/** A list of lists of integers */
public static class Partitioning extends ArrayList<Partition>
implements Comparable<Partitioning> {
public Partitioning() {
super();
}
public Partitioning(int N) {
super(N);
// Pre-allocate sub-lists for convenience
for (int j = 0; j < N; j++) {
add(new Partition());
}
}
// Lexicographic comparator
@Override
public int compareTo(Partitioning other) {
for (int i = 0, ii = Math.min(this.size(),
other.size()); i < ii; i++) {
int c = this.get(i).compareTo(other.get(i));
if (c != 0) {
return c;
}
}
return Integer.compare(this.size(), other.size());
}
}
/** Print all unique partitionings of the passed array of integers */
public static void printPartitionings(int[] elts) {
int N = elts.length;
Set<Partitioning> setOfPartitionings = new HashSet<>();
// Generate integers in [0, N^N)
for (long i = 0, ii = (long) Math.pow(N, N); i < ii; i++) {
// Create empty partitioning
Partitioning partitioning = new Partitioning(N);
// Assign each element to a partition based on base N digit
long digits = i;
for (int j = 0; j < N; j++) {
int digit = (int) (digits % N);
digits /= N;
partitioning.get(digit).add(elts[j]);
}
// Sort individual partitions, and remove empty partitions
Partitioning partitioningSorted = new Partitioning();
for (Partition partition : partitioning) {
if (!partition.isEmpty()) {
Collections.sort(partition);
partitioningSorted.add(partition);
}
}
// Sort the partitioning
Collections.sort(partitioningSorted);
// Add the result to the final set of partitionings
setOfPartitionings.add(partitioningSorted);
}
// Sort lexicographically to make it easier to view the result
List<Partitioning> setOfPartitioningsSorted = new ArrayList<>(
setOfPartitionings);
Collections.sort(setOfPartitioningsSorted);
for (Partitioning partitioning : setOfPartitioningsSorted) {
System.out.println(partitioning);
}
}
public static void main(String[] args) {
printPartitionings(new int[] { 1, 2, 1, 2 });
}
}
实现没有特别优化-可以通过多种方式使其更快。同样,所示代码仅适用于中等大小的问题,当N ^ N <Long.MAX_VALUE
时,即所示代码的N的最大值为15(但您不希望运行该问题)还是要调整大小,因为代码要花很长时间才能运行。
输出:
[[1], [1], [2], [2]]
[[1], [1], [2, 2]]
[[1], [1, 2], [2]]
[[1], [1, 2, 2]]
[[1, 1], [2], [2]]
[[1, 1], [2, 2]]
[[1, 1, 2], [2]]
[[1, 1, 2, 2]]
[[1, 2], [1, 2]]
对于输入{ 1, 2, 3, 2 }
,输出为:
[[1], [2], [2], [3]]
[[1], [2], [2, 3]]
[[1], [2, 2], [3]]
[[1], [2, 2, 3]]
[[1, 2], [2], [3]]
[[1, 2], [2, 3]]
[[1, 2, 2], [3]]
[[1, 2, 2, 3]]
[[1, 2, 3], [2]]
[[1, 3], [2], [2]]
[[1, 3], [2, 2]]