📜  自适应霍夫曼编码和解码

📅  最后修改于: 2021-04-23 07:08:21             🧑  作者: Mango

先决条件:霍夫曼编码,霍夫曼解码
自适应霍夫曼编码也称为动态霍夫曼编码。该实现是使用Vitter算法完成的。

编码方式

包含字母的字符串的自适应霍夫曼编码:
令m为字母总数。所以m = 26
对于Vitter Algorithm,找到参数e&r使得

m = 2e + r and 0 ≤ r ≤ 2e
Therefore, for m = 26 we get e = 4 & r = 10

有两种类型的代码:NYT代码和固定代码。

NYT code = Traversing tree from the root node to that particular NYT node.

对于固定代码,可以从以下两个条件计算得出:

  1. 如果0≤k≤2r,则字母Sk被编码为(e + 1)位中(k-1)的二进制表示。 (其中k是按字母顺序排列的字母的位置)
  2. 否则,将字母Sk编码为e位的(kr-1)的二进制表示形式。

更新树木
Vitter算法中的树更新遵循隐式编号。在隐式编号中

  • 节点以递增顺序编号,即按级别和从左到右的顺序
  • 具有相同权重和类型的节点一起形成一个块
  • 通过增加权重的顺序,块彼此相关
  • 内部节点由椭圆形表示。内部节点的权重=子节点权重的总和
  • 外部节点由正方形表示。外部节点的权重=最初为1,如果重复,则将权重增加1

更新树的步骤:

  1. 使用NYT节点初始化树
  2. 对于首次识别的符号,将初始NYT节点进一步划分为NYT节点,新节点初始化为该符号,权重= 1。
  3. 将子节点权重之和分配给父节点
  4. 如果遇到重复的符号,则权重将更新为该符号。

注意:在“树中更新”期间,如果左侧子树的权重大于右侧子树的权重,则必须交换节点。

例子

code = "aardvark"
The final Code we get is:
00000 1 010001 0000011 0001011 0 10  110001010
  a   a   r      d        v    a  r     k

解释:
对于字符串代码=“ aardvark”,e = 5,r = 10
如上图所示,使用权重为0的NYT节点初始化树。

  1. 对于符号’a’,k = 1。
    NYT Code = "" (initially tree is empty)
    

    对于固定代码:由于k <2r,即1 <2 * 10,满足条件(1)
    因此固定代码是(k-1)= 0的二进制表示形式为5位表示形式

    Fixed Code = "00000"
    Huffman Code for symbol for 'a' is "00000"
  2. 对于树中已经存在的符号“ a”。遍历树到符号“ a”,我们得到的代码为“ 1”
    Huffman Code for symbol for 'a' is "1"
  3. 对于符号’r’,k = 18。
    NYT Code = "0" (traversing up to NYT Node)

    对于固定代码:当k> 2r,即18> 2 * 10时,满足条件(2)
    因此固定代码是(k-1 = 17)的二进制表示形式为5位表示形式

    Fixed Code = "10001"
    Huffman Code for symbol for 'r' is "010001"
  4. 对于符号“d”,k = 4。
    NYT Code = "000" (traversing up to NYT Node)

    对于固定代码:由于k <2r,即4 <2 * 10,满足条件(1)
    因此固定代码是(k-1 = 3)的二进制表示形式为5位表示形式

    Fixed Code = "00011"
    Huffman Code = "00000011"
  5. 对于符号’v’,k = 22。
    NYT Code = "000" (traversing up to NYT Node)

    对于固定代码:当k> 2r,即22> 2 * 10时,满足条件(2)
    因此固定代码是(kr-1 = 11)的二进制表示形式为4位表示形式

    Fixed Code = "1011"
    Huffman Code = "0001011"
  6. 交换左子树的节点和右树,因为它违反了属性
  7. 对于树中已经存在的符号“ a”。遍历树直到符号“ a”,我们得到的代码为“ 0”
    Huffman Code for symbol for 'a' is "0"
  8. 对于树中已经存在的符号“ r”。遍历树直到符号“ a”,我们得到的代码为“ 10”
    Huffman Code for symbol for 'r' is "10"
  9. 对于符号’k’,k = 11。
    NYT Code = "1100" (traversing up to NYT Node)

    对于固定代码:由于k <2r,即11 <2 * 10,满足条件(1)
    因此固定代码是(k-1 = 10)的二进制表示形式为5位表示形式

    Fixed Code = "01010"
    Huffman Code for symbol for 'r' is "110001010"

解码

解码步骤:

  1. 读取二进制字符串
  2. 如果遇到的叶节点是NYT
    • 读取下一个e位
      1. 如果e位值
      2. 如果e位的值> r,则为了获得所需的符号,将e位转换为e位的十进制值+ r + 1

例子:

code = "00000101000100000110001011010110001010"
We get final decoded code as
 00000  1   0    10001  00   00011  000  1011  0  10  1100  01010
   a    a  NYT     r    NYT    d    NYT   v    a   r   NYT    k

解释:

  • 通过读取第一个e位开始解码。因此,前4位为0000,转换为十进制= 0。
    现在值0 现在根据条件(1),将第一个e + 1 = 5位转换为十进制并加1。
    00000 = 0
    0 + 1 = 1, which is value for alphabet a.
    

    更新树并在树中添加符号“ a”的节点

  • 读取给定代码中的下一位并遍历树。我们到达外部叶子节点“ a”。因此,下一个已解码符号是“ a”。
  • 读取给定代码的下一组位并遍历树。我们将0作为NYT节点。到达NYT节点后,读取e位,它们是1000。将1000转换为十进制是8。由于8 现在,将e + 1位转换为十进制并加1。
    10001 = 17
    17 + 1 = 18, which is value for alphabet r.
    

    更新树,并在树中为符号“ r”添加一个节点。

  • 读取下一组位并遍历树,我们到达00处的NYT节点。读取e位为0001。将0001转换为十进制为1。由于1 现在,将e + 1位转换为十进制并加1。
    00011 = 3
    3 + 1 = 4, which is value for alphabet d.
    

    更新树并在树中为符号“ d”添加一个节点。

  • 读取下一组位并遍历Tree,我们到达000处的NYT节点。读取e位为1011。将1011转换为十进制为11。当11> r满足条件(2)时。
    现在,以十进制转换k + r + 1位并解码该符号。
    10110 = 22, which is value for alphabet v.
    

    更新树,并在树中为符号“ v”添加一个节点。

  • 读取下一组位并遍历树,我们在0处得到符号’a’。更新树并在树中添加符号’a’的节点。
  • 读取下一组位并遍历树,我们在10获得符号“ r”。更新树并在树中添加符号“ a”的节点。
  • 读取下一组位并遍历树,我们在1100到达NYT节点。读取e位0101。将0101转换为十进制为9。9 现在,将e + 1位转换为十进制并加1。
    01000 = 8, 
    8 + 1 = 9. which is value for alphabet k.
    

    更新树,并在树中为符号“ v”添加一个节点。