我目前正在进行一个霍夫曼文本压缩练习,但是我遇到了一些编码问题的问题。我使用字符频率表来保存我需要的信息作为标题用于解压缩文件(全部转换为二进制字符串,然后保存在字节数组中)。
首先,我为每个字符使用了2个字节,字符使用了1个字节,频率使用了1个字节。但是我意识到它不适用于某些字符频率超过255(1字节)的大文本。
因此我做了修改,根据频率调整每个字符的保留字节数。它看起来像这样:
public String freString(int freq, String s){
freq = freq - 255;
s = s + ("11111111");
if(freq>=255){
s = freString(freq,s);
}else{
String remainFreq= Integer.toBinaryString(freq);
//patch the ommited zeros of last byte
remainFreq= String.format("%8s", remainFreq);
remainFreq= tempString.replace(' ', '0');
s = s + remainFreq;
}
return s;
}
有了这个,在解压缩期间,我将查看下一个字节,看它的值是什么,如果是255,那么继续添加下一个字节的值......等等。
标题示例:
[9, 141 ,3, 142 ,255,33, 143 ,255,255,2]
[标题长度= 9, a = 3, b = 288, c = 512]
它工作正常,但随着文本越来越大,它大大降低了我的压缩率。 例如:如果'a'重复5000次。我将使用最多 20个字节来存储我的频率值字符串,而不是 2个字节(00010011 10001000 = 5000)
所以这是我的问题......有没有更好的策略可以用来动态增加字符的保留字节,同时指示“freq字符串结束”?我虽然每个字符保留最少3个字节(1表示char,1表示freq,1表示freq字符串结束),但这会影响较小文本文件的压缩率。 这是我必须采取的权衡吗?或者有更好的方法吗?
答案 0 :(得分:0)
如果你有一个霍夫曼树,那么你可以制作许多其他霍夫曼树,通过交换任何节点的左右子节点,为所有符号分配相同的长度但不同的代码字。所有这些树都同样好 - 它们压缩数据同样多,因为长度保持不变。 Canonical Huffman是一种事先同意如何从所有可能的置换树中选择一棵特定树的方法,这样你就不必知道你实际使用的那棵树中的哪一棵。
在实践中,这意味着可以从长度上重建树。 实际重建树没有必要,但重建它的能力意味着您保留了所有信息。
通常情况下,对于哪个树是规范树,您可以做出不同的选择。您所做的选择对解码技术有一些影响,但这可能超出了这个问题的范围。无论如何,一种选择是置换树,以便
规则1和2制作一侧最深的树,另一侧最浅的树,其间没有奇怪的跳跃。规则3命令处于相同深度的节点。
事实证明,你不需要进行任何树重组。只使用分配给每个符号的长度,可以轻松构建代码,如下所示:
// create histogram of lengths
const int maxcodelength = 15; // 15 is the max code length for Deflate
uint[] length_count = new uint[maxcodelength + 1];;
for (int i = 0; i < symbollengths.Length; i++)
length_count[symbollengths[i]]++;
// find starting point (lowest code) for each length
uint code = 0;
uint[] next_code = new uint[maxcodelength + 1];
next_code[maxcodelength] = 0;
for (int bits = maxcodelength - 1; bits >= 0; bits--)
{
code = (code + length_count[bits + 1]) >> 1;
next_code[bits] = code;
}
// assign codes to symbols
uint[] codes = new uint[256];
for (int n = 0; n < codes.Length; n++)
{
int len = symbollengths[n];
if (len != 0)
{
codes[n] = next_code[len];
next_code[len]++;
}
}
这与rfc1951 (Deflate)第8页上的代码密切相关,但不同(转移是另一种方式,导致全零代码具有最长的长度,在Deflate中,全零代码具有最短的长度)。
至于标题,现在每个符号只需要4位(如果你也使用15的长度限制),每个符号肯定不超过8位(代码长于256会有点疯狂)。对于标题,这仍然是128或256字节(对于256的字母表)。你可以改进这一点,例如借用Deflate的游程长度编码方案。
其他内容。
保证不超过最大长度的一种方法是将所有频率除以2(向上舍入)并重新创建霍夫曼树,直到不再超过最大长度。还有其他方法可以计算有效的长度集,而无需构建树,例如package-merge。
几乎所有使用霍夫曼编码的压缩格式都限制了长度。它对编码和解码都很重要,主要用于解码。对于编码,代码不超过25意味着您可以使用32位缓冲区并写出字节(意味着缓冲区中最多可以保留7位),而无需特殊情况,因为在缓冲区中添加代码会溢出。对于解码,短(ish)最大代码长度允许简单的单表查找 - 它当时用maxcodelength
位(&#34;窗口&#34;)索引,给出第一个符号(窗口中的实际解码和该符号的长度(因此它可以移出)。如果最大代码长度较长,则需要稍微复杂的技术,例如多级表,或我个人喜欢的,不同的表,具体取决于窗口中前导零的数量。