我有一个信息检索应用程序,它创建大小为10万位的位数组。数组中“set”位的数量变化很大,从所有清除到全部设置。目前,我使用的是直接位数组(java.util.BitSet
),因此每个位数组都需要几兆字节。
我的计划是查看第一个 N 位的基数,然后决定使用哪个数据结构用于余数。显然,对于非常稀疏的位阵列,一些数据结构更好,而当大约一半的位被设置时,其他数据结构更好(当大多数位被设置时,我可以使用否定将其视为稀疏的零集合)。
以下是一些约束或提示:
使用开源Java实现的东西是有帮助的,但不是绝对必要的。我对基础知识更感兴趣。
答案 0 :(得分:16)
除非数据是真正随机的 且 具有对称的1/0分布,否则这只会成为无损数据压缩问题,并且非常类似于CCITT Group 3压缩用于黑白(即:二进制)传真图像。 CCITT Group 3使用霍夫曼编码方案。在传真的情况下,他们使用一组固定的霍夫曼码,但对于给定的数据集,您可以为每个数据集生成一组特定的代码,以提高实现的压缩比。只要您只需按顺序访问位,就像您暗示的那样,这将是一种非常有效的方法。随机访问会产生一些额外的挑战,但你可能会生成一个二进制搜索树索引到数组中的各种偏移点,这样你就可以接近所需的位置,然后从那里进入。
注意 :只要1/0分布不是完全均匀,即使数据是随机的,霍夫曼方案仍然可以正常工作。也就是说,分布越均匀,压缩比越好。
最后,如果这些位是真正随机的,均匀分布,那么,根据先生。 Claude Shannon ,你无法使用任何方案压缩它。
答案 1 :(得分:4)
我强烈考虑使用范围编码代替霍夫曼编码。通常,范围编码可以比霍夫曼编码更有效地利用不对称性,但是当字母表大小如此之小时尤其如此。实际上,当“原生字母”只是0和1时,Huffman可以获得任何压缩的唯一方法就是组合这些符号 - 这正是编码将做的更有效的。
答案 2 :(得分:2)
对你来说可能为时已晚,但对于稀疏位数组(无损)和其他基于尝试的数据类型,存在一个非常快速且内存有效的库。看Judy arrays
答案 3 :(得分:1)
感谢您的回答。这就是我要尝试动态选择正确的方法:
我将收集常规位数组中的所有第一个 N 命中,并根据此样本的对称性选择三种方法之一。
非对称区域,中等区域和对称区域之间的边界将取决于各种算法所需的时间与所需空间的平衡,其中时间与空间的相对值将是可调参数。霍夫曼编码所需的空间是对称性的函数,我将通过测试对其进行分析。另外,我将测试所有三种方法,以确定实现的时间要求。
有可能(实际上我希望)中间压缩方法总是比列表或位数组或两者都好。也许我可以通过选择适合更高或更低对称性的一组霍夫曼代码来鼓励这一点。然后我可以简化系统,只使用两种方法。
答案 4 :(得分:1)
另一个压缩思想:
如果位数组不长,你可以尝试在使用任何重复编码之前应用Burrows-Wheeler transform,例如Huffman。一个简单的实现将在(de)压缩期间花费O(n ^ 2)内存并且在O(n ^ 2 log n)时间内进行解压缩 - 几乎可以肯定有快捷方式。但是如果你的数据有任何顺序结构,这应该真的有助于霍夫曼编码。
您也可以一次将该想法应用于一个块,以使时间/内存使用更加实用。如果您正在按顺序读/写,那么在一次使用一个块可以让您始终保持大部分数据结构被压缩。
答案 5 :(得分:0)
直接无损压缩是可行的方法。要使其可搜索,您将必须压缩相对较小的块并在块的数组中创建索引。该索引可以包含每个块中起始位的位偏移量。
答案 6 :(得分:0)
快速组合证明你无法真正节省太多空间:
假设您有n个2位的任意子集设置为n个总位数中的1个。你有(n选择n / 2)种可能性。使用Stirling's formula,这大约是2 ^ n / sqrt(n)* sqrt(2 / pi)。如果每种可能性同样可能,那么就没有办法给出更可能的选择更短的表示。所以我们需要log_2(n选择n / 2)位,大约是n - (1/2)log(n)位。
这不是很好的记忆储蓄。例如,如果你正在使用n = 2 ^ 20(1 meg),那么你只能保存大约10位。这不值得。
说完这一切之后,任何真正有用的数据似乎都不太可能是真正随机的。如果您的数据有任何更多结构,可能会有更乐观的答案。