【问题标题】:Creating combinations of a BitSet创建 BitSet 的组合
【发布时间】:2019-08-22 08:22:18
【问题描述】:

假设我有一个 Java BitSet。我现在需要对 BitSet 进行组合,以便只有设置的位可以翻转。即只需要设置的位组合。

例如。 BitSet - 1010,组合 - 1010, 1000, 0010, 0000

BitSet - 1100,组合 - 1100、1000、0100、0000

我可以想到一些解决方案,例如我可以采用所有 4 位的组合,然后将这些组合与原始 Bitset 进行异或。但这对于大型稀疏 BitSet 来说将是非常耗费资源的。所以我一直在寻找更优雅的解决方案。

【问题讨论】:

  • 所以你想要一个bitset的powerset?

标签: java combinations bitset


【解决方案1】:

您似乎想要获取位集的power set。关于如何获得Set<T> 的电源组,已经有here 的答案。在这里,我将展示该帖子中显示的算法的修改版本,使用BitSets:

private static Set<BitSet> powerset(BitSet set) {
    Set<BitSet> sets = new HashSet<>();
    if (set.isEmpty()) {
        sets.add(new BitSet(0));
        return sets;
    }
    Integer head = set.nextSetBit(0);
    BitSet rest = set.get(0, set.size());
    rest.clear(head);
    for (BitSet s : powerset(rest)) {
        BitSet newSet = s.get(0, s.size());
        newSet.set(head);
        sets.add(newSet);
        sets.add(s);
    }
    return sets;
}

【讨论】:

  • 非常感谢!正是我需要的:D
【解决方案2】:

如果您意识到整数是计算机的“开关”模式的内在变体,并且在适当的整数范围内迭代最终将产生所有可能的排列,您可以在单个线性传递而不是递归中执行操作。在您的情况下,唯一的挑战是将整数的密集位传输到 BitSet 的目标位。

这是一个这样的解决方案:

static List<BitSet> powerset(BitSet set) {
    int nBits = set.cardinality();
    if(nBits > 30) throw new OutOfMemoryError(
        "Not enough memory for "+BigInteger.ONE.shiftLeft(nBits)+" BitSets");
    int max = 1 << nBits;
    int[] targetBits = set.stream().toArray();
    List<BitSet> sets = new ArrayList<>(max);
    for(int onOff = 0; onOff < max; onOff++) {
        BitSet next = new BitSet(set.size());
        for(int bitsToSet = onOff, ix = 0; bitsToSet != 0; ix++, bitsToSet>>>=1) {
            if((bitsToSet & 1) == 0) {
                int skip = Integer.numberOfTrailingZeros(bitsToSet);
                ix += skip;
                bitsToSet >>>= skip;
            }
            next.set(targetBits[ix]);
        }
        sets.add(next);
    }
    return sets;
}

它使用 int 值进行迭代,这已经足以表示可以存储在 Java 内置集合之一中的所有排列。如果您的源 BitSet 有 2³¹ 个位,则 2³² 可能的组合不仅需要 100 GB 堆,而且还需要支持 2³² 元素的集合,即不能用 int 表示的大小。

因此,如果数量超出能力,上面的代码会提前终止,甚至不需要尝试。您可以将其重写为使用long 甚至BigInteger,以使其在这种情况下保持忙碌,直到它无论如何都会因OutOfMemoryError 而失败。

对于工作案例,int 解决方案是最有效的变体。

请注意,代码返回 List 而不是 HashSet 以避免散列成本。已知这些值是唯一的,只有当您想要执行查找时,散列才会得到回报,即用另一个 BitSet 调用 contains。但是要测试现有的BitSet 是否是您的输入BitSet 的排列,您甚至不需要生成所有排列,一个简单的位操作,例如andNot 已经告诉你了。因此,对于存储和迭代排列,ArrayList 更有效。

【讨论】:

    猜你喜欢
    • 2020-01-13
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2016-08-30
    • 1970-01-01
    • 2019-09-13
    相关资源
    最近更新 更多