Algorithm 集合压缩算法

Algorithm 集合压缩算法,algorithm,language-agnostic,compression,trie,Algorithm,Language Agnostic,Compression,Trie,我有一套我想放在一个房间里 正常的尝试是由元素字符串组成的——也就是说,元素的顺序很重要。集合缺少定义的顺序,因此有可能进行更大的压缩 例如,给定字符串“abc”、“bc”和“c”,我将创建trie: (*,3) -> ('a',1) -> ('b',1) -> ('c',1) -> ('b',1) -> ('c',1) -> ('c',1) 但是给定集合{'a',b',c'},{'b',c'},{'c'},我可以创建上面的trie,

我有一套我想放在一个房间里

正常的尝试是由元素字符串组成的——也就是说,元素的顺序很重要。集合缺少定义的顺序,因此有可能进行更大的压缩

例如,给定字符串
“abc”
“bc”
“c”
,我将创建trie:

(*,3) -> ('a',1) -> ('b',1) -> ('c',1)
      -> ('b',1) -> ('c',1)
      -> ('c',1)
但是给定集合
{'a',b',c'}
{'b',c'}
{'c'}
,我可以创建上面的trie,或者这十一个集合中的任何一个:

(*,3) -> ('a',1) -> ('b',1) -> ('c',1)
      -> ('c',2) -> ('a',1)

(*,3) -> ('a',1) -> ('c',1) -> ('b',1)
      -> ('b',1) -> ('c',1)
      -> ('c',1)

(*,3) -> ('a',1) -> ('c',1) -> ('b',1)
      -> ('c',2) -> ('a',1)

(*,3) -> ('b',2) -> ('a',1) -> ('c',1)
                 -> ('c',1)
      -> ('c',1)

(*,3) -> ('b',1) -> ('a',1) -> ('c',1)
      -> ('c',2) -> ('b',1)

(*,3) -> ('b',2) -> ('c',2) -> ('a',1)
      -> ('c',1)

(*,3) -> ('b',1) -> ('c',1) -> ('a',1)
      -> ('c',2) -> ('b',1)

(*,3) -> ('c',2) -> ('a',1) -> ('b',1)
      -> ('b',1) -> ('c',1)

(*,3) -> ('c',2) -> ('a',1) -> ('b',1)
                 -> ('b',1)

(*,3) -> ('c',2) -> ('b',1) -> ('a',1)
      -> ('b',1) -> ('c',1)

(*,3) -> ('c',3) -> ('b',2) -> ('a',1)
因此,显然有压缩的空间(7个节点到4个节点)

我怀疑在每个节点上定义一个依赖于其子节点的相对频率的局部顺序可以做到这一点,但我不确定,这可能过于昂贵


所以在我打开白板,开始研究我自己的压缩算法之前,有没有一个现有的算法?它有多贵?这是一个批量过程,还是每次插入/删除都可以完成?

基本上,您应该构建一个依赖关系图。如果元素y仅在x出现时出现,则从x到y绘制一条边(如果相等,则按字典顺序排列)。生成的图形是DAG。现在,对该图进行拓扑排序,以获得元素的扭曲顺序。只要你能从两个(或更多)元素中选择一个,就选择出现次数较多的元素。

我认为你应该根据项目频率对集合进行排序,这会得到一个很好的启发。在(频繁模式挖掘)中使用相同的方法以紧凑的方式表示项目集

我的猜测是,最大压缩将使最常见的元素保持在顶部(如上一个示例所示)

压缩算法将从整个集合和顶部节点开始,并递归地为包含最常见元素的每个子集创建节点

Compress(collection, node):
    while NOT collection.isEmpty? 
      e = collection.find_most_common_element
      c2 = collection.find_all_containing(e)
      collection = collection - c2
      if e==NIL //empty sets only
         node[END_OF_SET]=node
      else
        c2.each{set.remove(e)}
        node[e]=new Node
        Compress(c2,node[e])
      end
    end
生成的树将有一个特殊的集合结束标记,表示一个完整集合在该节点结束。你的例子是

 *->(C,3)->(B,2)->(A,1)->EOS
                ->EOS
          ->EOS

删除一个集合很容易,只需删除它的EOS标记(以及任何变为空的父节点)。您可以动态插入-在每个节点上,下降到子元素最多的匹配元素,直到没有匹配,然后使用上面的算法-但保持最大压缩会很棘手。当元素B获得的子元素多于元素A时,您必须将包含A和B的所有集合移动到B节点中,这将涉及对A的所有子元素的完整搜索。但如果不压缩,则包含搜索将不再与集合大小成线性关系。

我认为trie不是表示集合的很好结构。一组位数组不是更好吗?你希望做什么手术?你为什么这么担心内存?@svick:也许吧,但我的集合是从大量元素中提取的,所以位数组可能不是很有效。迭代(子集、频率)对。因为我有很多数据。你打算做什么操作?传统的trie可以有效地告诉您给定的字符串是否包含在它所表示的字符串集中。如果您的trie对其字符串重新排序以最小化结构大小,您如何实际测试trie中是否包含给定的字符集?看起来您需要搜索每个排列。@Weeble:您可以在每个节点上存储本地顺序以及每个节点的子节点。在查找节点时,迭代其子节点,并遍历到与包含的元素匹配的第一个节点。您是只希望压缩以便以后解压缩,还是还希望对压缩的结构执行集合操作?整圈!我实际上是在看这个,因为我认为FP增长中使用的全局顺序是不够的。可能你可以重建子树,根据这个子树中的项目频率,它可以给你更好的压缩,但在这种情况下,我们需要执行更多的计算。