【问题标题】:Creating all multisets of subsets of NON DISTINCT values, in Java在 Java 中创建所有 NON DISTINCT 值子集的多集
【发布时间】:2019-03-11 10:34:32
【问题描述】:

给定一个对象数组,当数组中的值可能重复时,我需要尽可能高效地找到给定数组的所有不同子集集,包括所有值。

例如:如果数组是1, 2, 1, 2,那么我需要创建以下多重集:

  1. {[1], [1], [2], [2]}
  2. {[1], [1], [2, 2]}
  3. {[1], [2], [1, 2]}
  4. {[1], [1, 2, 2]}
  5. {[1, 1], [2], [2]}
  6. {[1, 1], [2, 2]}
  7. {[1, 2], [1, 2]}
  8. {[1, 1, 2], [2]}
  9. {[1, 1, 2, 2]}

请注意,子集中值的顺序和多重集中子集的顺序都不重要。像{[1, 2, 2], [1]} 这样的多集与#4 相同,而{[2, 1], [2], [1]}#3 相同。

这里的例子是整数,但实际上我必须用对象来做。

这应该尽可能高效。最好只计算正确的(不重复的)多重集,而不检查是否已经出现,因为创建它的方式将消除这种情况。

我知道如何使用二进制表示创建所有子集。我用它结合递归来计算所有多重集。这完美地工作,除了当值重复时它不起作用。这是我到目前为止所做的:

(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,1,2,3],从该集合中创建所有子集,然后简单地将每个索引替换为其原始列表中的相应元素。
  • 也许有一些聪明的方法可以优化为范围创建多组子集。

标签: java set subset multiset


【解决方案1】:

这是“幂集”问题,不止两个通常的集合(通常包含在子集中或排除在子集中,因此幂集有 2^N 个元素)。对于您的这个问题的版本,任何元素都可以是最多 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]]

【讨论】:

  • 非常感谢!我刚刚阅读了您的回答,印象深刻。我现在要研究它。我将不胜感激有关优化代码以提高速度的建议。
  • 嗯,速度的最大问题是比例因子——O(N^N),这非常疯狂,超出了标准的指数 O(b^N)。你关心 N 的什么值?如果 N 很小(可能 N
猜你喜欢
  • 2017-07-11
  • 2012-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多