通过有效的连接和满足操作来代表反链

时间:2015-02-26 13:23:30

标签: java performance math optimization data-structures

一些信息

我正在开发一个适用于基本集和反链的程序。

Antichains是集合的powerset的子集,因此该子集中没有两个元素(集合)是该子集中的另一个元素(集合)的子集。例如,{{1},{1,2}}不是一个反社会因为{1}⊆{1,2}。

关于反链A和B的一些最重要的操作可以定义为

  1. a.join(b)= sup(a∪b)
  2. a.meet(b)= sup({X∩Y|X∈a和Y∈b})
  3. 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进行优化。

    我的问题

    1. 我现在的问题是BitSet是否是最佳表示,知道每次添加到起始集的元素时这些比特数表示的大小加倍。我还想到了BigInteger,具有可比性的优势(我也需要)。
    2. 此外,我想知道是否有人已经做了一些事情,并且知道如何使用bitarray属性实现连接并有效地满足。
    3. 提前致谢。


      编辑:

      我的加入和见面的代码目前看起来像这样:

      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
      }
      

2 个答案:

答案 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是你的反链长度。你可能会更好

  • 如果每个antichain进行多次比较:只需调用toLongArray()并手动比较long
  • 如果每个antichain进行一次比较:手动从高位循环到低位并检查。

答案 1 :(得分:1)

  

现在我想提升这种表现方式以类似的方式表示反链。这意味着我可以在一个bitarray中将antichain {{1},{2,3}}表示为1000010,因为存储集合{1}的长整数是1而对于{2,3}它是6(指数是1位于比特阵中。

这听起来不对:{{1, 64}}怎么样? IIUYC指数是2 ** 63 + 1,对于BitSet来说太大了。如果你想要一个优化的表示,请考虑一些原始的长集合(trove4j,colt,hppc,...)。

  
      
  1. 为了能够比较我的bitarray,有没有更有效的方法将BitSet转换为BigInteger?
  2.   

最有效的方法肯定是避免转换。 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)