我正在开发一个适用于基本集和反链的程序。
Antichains是集合的powerset的子集,因此该子集中没有两个元素(集合)是该子集中的另一个元素(集合)的子集。例如,{{1},{1,2}}不是一个反社会因为{1}⊆{1,2}。
关于反链A和B的一些最重要的操作可以定义为
sup是supremum的一个antihain,意思是最小的antichain大于给定的集合。
基本集由long
表示,类似于bitarray。这意味着该组的每个元素在比特阵列中由1表示。例如,集合{1,2,3}由7(比特阵列111)表示,集合{1,2,4}由11表示(比特阵列1011),依此类推。
现在我想提升这种表现方式以类似的方式表示反链。这意味着我可以在一个bitarray中将antichain {{1},{2,3}}表示为1000010,因为存储集合{1}的长整数是1而对于{2,3}它是6(指数是比特币中的1)
除非我找到更好的替代方案,否则我使用BitSet - 类来处理这个比特阵,希望节省一些时间来处理任何Collection<T>
。
我已经设法定义和优化之前陈述的大部分基本操作,但它们在较旧的实现中进行了优化,只使用TreeSet
,因此未针对使用bitarray进行优化。
BigInteger
,具有可比性的优势(我也需要)。 提前致谢。
我的加入和见面的代码目前看起来像这样:
public AntiChain join(AntiChain ac) {
AntiChain res = new AntiChain(this);
for(int i = ac.bitset.nextSetBit(0); i >= 0; i = ac.bitset.nextSetBit(i+1)) {
res.addAndMakeAntiChain(new BasicSet(i));
}
return res;
}
public AntiChain meet(AntiChain ac) {
AntiChain res = AntiChain.emptyAntiChain(this.getUniverse());
for(int i = bitset.nextSetBit(0); i >= 0; i = bitset.nextSetBit(i+1))
for(int j = ac.bitset.nextSetBit(0); j >= 0; j = ac.bitset.nextSetBit(j+1))
res.addAndMakeAntiChain(new BasicSet(j).intersection(new BasicSet(i)));
return res;
}
private void addAndMakeAntiChain(BasicSet x) {
for(int k = bitset.nextSetBit(0); k >= 0; k = bitset.nextSetBit(k+1)) {
BasicSet a = new BasicSet(k); //new BasicSet(7) = {1,2,3}
if(a.hasAsSubset(x)) return;
if(x.hasAsSubset(a)) bitset.set(k, false);
}
bitset.set(x.toIntRepresentation()); //{1,2,3}.toLong() = 7
}
答案 0 :(得分:1)
好吧,我可以想出一种更好的存储你的反链的方法(仍然在BitSets中)。目前,您的套装将非常稀疏 - 高比例为0:1。
我只是将它们连接起来。保证每个值都小于64
位,因为您使用long
值表示它们,所以我会做这样的事情
public static void put(BitSet antiChain, long value) {
int len = antiChain.length();
antiChain.clear(antiChain.size() - 1);
len -= len % 64;
for (int i = 0; i < 64; i++)
if ((value & (1 << i)) != 0) antiChain.set(len + i);
antiChain.set(antiChain.size());
}
public static long get(BitSet antiChain, int index) {
long value = 0;
for (int i = 0; i < 64; i++)
if (antiChain.get(index * 6 + i)) value |= (1 << i);
return value;
}
public static boolean contains(BitSet antiChain, long value) {
for (int i = 0; i < antiChain.size() / 64; i++) {
if (get(antiChain, i) == value) return true;
}
return false;
}
在最后确实包含一个设置位,无论确保包含的数量是显式的;即{} != {{}}
举一个具体的例子,这里{{1},{2,3}}
会发生什么。 (请注意,s(n)
是指n
引用的集合。
{{1},{2,3},{4,5}} = {s(0b10), s(0b110), s(0b110000)} = {s(2), s(6), s(48)}
put
函数会将其编码为2*2^(64*0) + 6 * 2^(64*1) + 48*2^(64*2)
(其中^
表示取幂,而不是异或)。BitSet
为{1, 33, 64, 65, 66, 97, 98, 128, 132, 133, 164, 165, 256}
此外,无论您将BitSet
转换为BigInteger
的效率如何,compare
仍会在O(L)
时间内进行,其中L
是你的反链长度。你可能会更好
toLongArray()
并手动比较long
值答案 1 :(得分:1)
现在我想提升这种表现方式以类似的方式表示反链。这意味着我可以在一个bitarray中将antichain {{1},{2,3}}表示为1000010,因为存储集合{1}的长整数是1而对于{2,3}它是6(指数是1位于比特阵中。
这听起来不对:{{1, 64}}
怎么样? IIUYC指数是2 ** 63 + 1,对于BitSet
来说太大了。如果你想要一个优化的表示,请考虑一些原始的长集合(trove4j,colt,hppc,...)。
- 为了能够比较我的bitarray,有没有更有效的方法将BitSet转换为BigInteger?
醇>
最有效的方法肯定是避免转换。 BitSet
可以迭代(在两个方向上),因此您可以直接进行字典比较。
BigInteger result = BigInteger.ZERO;
for(int i = theAntiChain.nextSetBit(0); i >= 0; i = theAntiChain.nextSetBit(i+1))
result = result.setBit(i);
return result;
效率非常低,您可以改为创建byte[]
,填充它,然后使用new BigInteger(int signum, byte[] magnitude)
。