zoukankan      html  css  js  c++  java
  • 【数据压缩】Huffman编码

    1. 压缩编码概述

    数据压缩在日常生活极为常见,平常所用到jpg、mp3均采用数据压缩(采用Huffman编码)以减少占用空间。编码(C)是指从字符空间(A)到码字表(X)的映射。数据压缩编码指编码后信息的长度较于原始信息要短。本文试图探讨Huffman编码是如何保证唯一可译性、如何压缩、以及压缩效率如何?

    前缀码

    前缀码的任意一码字均不为其他码字的前缀,此保证了编码的唯一可译性。比如码字表{0, 01, 11, 1}001的前缀,111的前缀;当遇到字符文本011100,是应分隔为01-11-0-0还是0-11-1-0-0等?若采用前缀码编码,码字表为{0, 10, 11},则字符文本011100可即时分隔为0-11-10-0可译,所以前缀码亦被称为即时码。同时,前缀码保证了编码的唯一可译性,即字符空间(A)到码字表(X)的映射为一一映射。本文探讨的Huffman编码即为前缀码。

    根据码字长度,编码分为等长编码与变长编码。等长编码即字母表中所有码字的长度均相等,最为常见的是字长7位的ASCII码。变长编码则是码字的长度可能存在不相等。

    前缀码可表示为叶子节点为码字的编码二叉树,如图所示。

    期望编码长度

    如上图所示的两种变长编码,哪一种编码压缩效率比较好?显然,若信息编码之后的长度越小,则编码的压缩效率越好。为此,我们引出刻画量度期望编码长度

    首先我们定义字符空间(A = lbrace a_1,a_2, cdots ,a_n brace),即信息文本中有n个字符,且字符(a_i)的长度为(l_i),出现频率(即概率)为(p_i);则期望编码长度为

    [L = sumlimits_{i = 1}^n {p_i*l_i} ]

    若要期望编码长度(L)越小,学过数学的都知道,则高概率的码字字长应不长于低概率的码字字长,即满足

    [forall i,j p_i ge p_j Leftrightarrow l_i le l_j ]

    最优编码

    对于二元编码(01)的前缀码,满足McMillan-Kraft不等式

    [sumlimits_{i = 1}^n {{2^{ - l_i}}} le 1 ]

    具体的证明参看[3]。McMillan-Kraft不等式从整体上限制编码长度的下界。

    如下图所示的前缀码即满足McMillan-Kraft不等式。

    最优编码指期望编码长度最小的编码,求解最优编码等价于数学问题:

    egin{align}
    & min sumlimits_{i = 1}^n {{p_i}*{l_i}} cr
    & s.t. sum {{2^{ - {l_i}}}} le 1 label{eq:kraft}
    end{align}

    运用拉格朗日乘子法,构造目标函数
    egin{equation}
    J = sum {p_i*l_i + lambda (sum {{2^{ - l_i}}} } )
    end{equation}

    (l_i)求偏导,

    [{{partial J} over {partial l_i}} = p_i - lambda {2^{ - l_i}}ln 2 ]

    令偏导为0,得到

    [{2^{ - l_i}} = {{p_i} over {lambda ln 2}} ]

    将其代入McMillan-Kraft不等式eqref{eq:kraft}中,得到(lambda = {1 over {ln 2}}),最优编码的码字长度
    egin{equation}
    l_i = - log _{2}p_i
    end{equation}

    最优编码的期望码字长度即为字符空间的熵:
    egin{equation}
    sumlimits_{i} {p_il_i = - sumlimits_{i} {p_i log p_i} } = H(A)
    end{equation}

    由此,定义编码的冗余度(Redundancy of a code),表示编码的冗余描述:
    egin{equation}
    ho = L - H(A)
    end{equation}

    可以证明,前缀码的编码长度满足不等式
    egin{equation}
    H(A) le L le H(A) + 1
    end{equation}

    因此,前缀码的冗余度满足(0 le ho le 1)

    2. Huffman编码

    Huffman编码采用小顶堆来优化编码二叉树的建立过程,确保低概率的码字字长不短于高概率的码字,具体编码过程如下:

    1. 将字符空间的字符以概率为关键值建立小顶堆;
    2. 依次取堆顶元素两次,将该两个字符合成一棵二叉树,根节点的关键值为两个字符的概率相加;然后将该新合成的二叉树做为节点插入到小顶堆中;
    3. 重复步骤2直至小顶堆中只有一个节点,此节点即为编码二叉树。

    编码二叉树建立过程如图所示

    此字符空间有9个字符,采用等长编码则需要(4) bit;Huffman编码的期望字长则为(2.77) bit;字符空间的熵为(2.69) bit;冗余度为(2.77-2.69=0.08) bit.

    Python 3.6实现Huffman编码,代码参考了rosettacode

    # -*- coding: utf-8 -*-
    # @Time    : 2017/1/21
    # @Author  : rain
    from collections import Counter
    from heapq import heapify, heappop, heappush
    
    
    def huffman_coding(message):
        freq = Counter(message)  # counter for every character
        min_heap = [[cnt, [ch, '']] for ch, cnt in freq.items()]
        heapify(min_heap)
        while len(min_heap) > 1:
            low1 = heappop(min_heap)
            low2 = heappop(min_heap)
            for pair in low1[1:]:  # update children node
                pair[1] += '0'
            for pair in low2[1:]:  # update children node
                pair[1] += '1'
            # push [low1_cnt+low2_cnt, low1's children, low2's children]
            heappush(min_heap, [low1[0] + low2[0]] + low1[1:] + low2[1:])
        vocabulary = {pair[0]: pair[1] for pair in min_heap[0][1:]}  # text -> code
        return vocabulary
    
    sentence = 'this is an example for huffman encoding'
    print(huffman_coding(sentence))
    

    上述实现中,并没有直接构建二叉树,而是用到了一个小技巧——在小顶堆中循环编码。Huffman编码存在一个缺点:解码端要根据码字与编码之间的映射关系才能解码,即解码端与编码端应共享一棵Huffman编码树。

    3. 参考资料

    [1] Huffman, David A. "A method for the construction of minimum-redundancy codes." Proceedings of the IRE 40.9 (1952): 1098-1101.
    [2] Cover, Thomas M., and Joy A. Thomas. Elements of information theory. John Wiley & Sons, 2012.
    [3] Bernd Girod, EE398A Image and Video Compression.

  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/en-heng/p/4980055.html
Copyright © 2011-2022 走看看