一些子大白话
初学阶段,若有错误感谢指正
规范霍夫曼编码提出的意义:
在没有规范的时候,编码端的码表解码端并不知道,如果再将码表传送,对于本身数据量不大的压缩来说,可能码表就会在存储中就占较大比重,反而是把相对不复杂的问题搞得麻烦。
那么我们想如果提前指定一个规则,让编码端按照这个规则编码,解码端按照这个规则解码,就可以不用再建立码表了。
规范霍夫曼编码
两个规则:
1.一组码中码字长度最小的第一个编码一定是0
2.当码字长度变化时,长码字是在上一个短码字基础上末尾+1后补0(码字长度变几位就补几位0);长度不变时直接+1(都是二进制运算)
举个栗子
假设一组码字码长为1 1 2 2 3 4
则码长最小是1,那么这个码字就是0;
第二个码字仍然是1,则根据规则2,可知这个码字是1;
第三个码字长度为2,则根据规则2前半部分,可知码长增加1位,则对应的码字应该是(哦豁,看来我一开始就不对了,码长1位的只能有1个,不然下面就不能继续了)
重新举个栗子
假设一组码字码长为1 2 3 4 4
根据上面的分析我们列表看一下
码长 | 对应码字 |
---|---|
1 | 0 |
2 | 10 |
3 | 110 |
4 | 1110 |
4 | 1111 |
一组码长为2 4 5 5 6
码长 | 对应码字 |
---|---|
2 | 00 |
4 | 0100 |
5 | 01010 |
5 | 01011 |
6 | 011000 |
那么如果一组码字其最长码字为8,我们就用8bit来记录每个码长有多少个码字。
比如一组码字码长为3 4 4 5 5 5 5 6 7 7 8 8 8
则用8bit记录:0 0 1 2 4 1 2 3
第一位0:码长为1的码字有0个
第二位0:码长为2的码字有0个
第三位1:码长为3的码字有1个
第四位2:码长为4的码字有2个
…
代码理解
图片来源于数据压缩课程课件
这里我们给了一组符号及对应码长,那么我们根据上面介绍的规则可以写出其对应的码字。
在代码之前我们要了解这几个数组的含义
-
first[ ]
first[n]=x:表示码长为n的码字的第一个码字为x(或者理解成码长为n的码字的最小值)
比如:first[3]=000,first[5]=10100… -
index[ ]
index[m]=y:表示码长为m的第一个码字在所有码字的第y个位置。
比如:index[3]=0,index[5]=9… -
table[]
table[t]=z:表示第t个位置的码字对应的符号为z;
比如table[0]=a,table[1]=b,table[2]=c…
int len = 1; //初始化码字长度len
int code = bs.ReadBit(); //设置一个按位读取的函数,让code每次在码流中读取一位
while(code >= first[len])
{
code << 1;
code |=(bs.ReadBit());
len++;
}
code >> 1;
len--;
//至此识别出来一个即时码
循环部分分析
假设输入码流为011000011010…
len | code | first[len] | code 比first[len]大小 |
---|---|---|---|
1 | 0 | 0 | 相等,循环继续 |
2 | 01(1) | 0 | 大于,循环继续 |
3 | 011(3) | 0 | 大于,循环继续 |
4 | 0110(6) | 0010(2) | 大于,循环继续 |
5 | 01100(12) | 10100(20) | 小于,循环结束 |
那么code >> 1后为0110,len=4;
int sym_index = index[len]+(code-first[len]);
int sym=table[sym_index];
len | index[len] | code | first[len] | sym_index |
---|---|---|---|---|
4 | 1 | 0110 (6) | 0010 (2) | 5 |
得出结果 table[5]=f;
由给出的符号和码字的对应可知,该代码可以正确解出。