【发布时间】:2015-09-29 14:32:12
【问题描述】:
背景:我在 C++ 上实现了一个通用 LZSS 后端(here 可用。我在这个版本中使用的匹配算法非常简单,因为它最初是为了压缩相对较小的用于相对古老的硬件(特别是 Mega Drive/Sega Genesis,其中 64kB 是整个主 RAM)的文件(最多 64kB)。
尽管如此,在我的实现中,一些文件的压缩时间太长了,大约几分钟。原因有两个:天真的匹配算法花费了大部分时间,但这特别是因为我从文件中构造了一个压缩图以实现最佳压缩。在分析器上,大部分时间都花在寻找匹配上,甚至使结果图的二次大小都相形见绌。
一段时间以来,我一直在研究几种可能的替代品;引起我注意的是dictionary-symbolwise flexible parsing using multilayer suffix trees。多层部分很重要,因为我感兴趣的 LZSS 变体之一对(位置、长度)使用可变大小的编码。
我当前的实现允许滑动窗口中的匹配项与预读缓冲区重叠,因此该输入:
aaaaaaaaaaaaaaaa
可以直接编码为
(0,'a')(1,0,15)
而不是
(0,'a')(1,0,1)(1,0,2)(1,0,4)(1,0,8)
这里,(0,'a') 表示将字符 'a' 编码为文字,而 (1,n,m) 表示“从位置 n 复制 m 个字符”。
问题:说了这么多,这是我的问题:我在后缀树上找到的每个资源似乎都暗示它们无法处理重叠的情况,而只允许您找到不重叠的匹配。当涉及后缀树时,研究论文、书籍甚至一些实现都给出了没有重叠的压缩示例,就好像它们是完美的压缩一样(我会链接到其中的一些,但我的声誉不允许这样做)。他们中的一些人甚至提到在描述基本压缩方案时重叠可能很有用,但在讨论后缀树时却奇怪地沉默了。
由于无论如何都需要扩充后缀树以存储偏移信息,这似乎是一个可以在查找匹配项时检查的属性——您将过滤掉从前瞻缓冲区开始的任何匹配项。并且树的构造/更新方式意味着,如果一条边将您带到与从前瞻开始的匹配对应的节点,则您将返回前一个节点,因为任何进一步的后代也将在前瞻中缓冲区。
我的方法是错误的还是不正确的?是否有 LZ77/LZSS 的实现或讨论,其后缀树提到匹配与前瞻缓冲区重叠?
【问题讨论】:
-
我没看懂第一部分。如果你有一个大文件,你应该把它切成更小的块,所以每个块不超过 64kb。因此,您的简单实现不应随着文件变大而呈指数级减慢。
-
我不认为它会触发最坏的情况,而是呈指数级放缓;最坏情况下的简单匹配是 O (mn)(m = 模式长度,n = 搜索文本的长度),并且对文件,所以我以 O (mnp) 结尾——除了文件的开头和结尾。有问题的 LZSS 变体有一个 8192 字节的搜索缓冲区(所以 n = 8192)和一个 256 字节的前瞻缓冲区(所以 m = 256)。对于长度为 p 的文件,最坏的情况是 O (2097152 p),这或多或少是观察到的。
-
从其他来源获取有趣的方法:github.com/mist64/pucrunch 非常棒。更多信息在这里:koti.kapsi.fi/a1bert/Dev/pucrunch
-
这是一个有趣的方法。顺便说一句,koti 链接给出了“未找到”错误(但我确实在 Achive.org 上找到了它)。
标签: c++ algorithm suffix-tree lossless-compression lz77