哈夫曼函数

哈夫曼编码是一种无损数据压缩技术,基于字符出现频率构建最优二叉树,频繁字符对应短编码,减少平均编码长度。1951年由哈夫曼提出,其在文本压缩、数据通信等领域广泛应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在计算机数据处理中,哈夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

例如,在英文中,e的出现机率最高,而z的出现概率则最低。当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。

历史

1951年,哈夫曼在麻省理工学院(MIT)攻读博士学位,他和修读信息论课程的同学得选择是完成学期报告还是期末考试。导师罗伯特·法诺(Robert Fano)出的学期报告题目是:查找最有效的二进制编码。由于无法证明哪个已有编码是最有效的,哈夫曼放弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法,并很快证明了这个方法是最有效的。哈夫曼使用自底向上的方法构建二叉树,避免了次优算法香农-范诺编码(Shannon–Fano coding)的最大弊端──自顶向下构建树。

1952年,于论文《一种构建极小多余编码的方法》(A Method for the Construction of Minimum-Redundancy Codes)中发表了这个编码方法。

应用

1、哈夫曼编码

在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。

为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。

在MATLAB中进行哈夫曼编码,不需要直接使用内置的`huffmandict`或`huffmanenco`函数,可以手动实现基本步骤: 1. **构建频率统计表**:首先,需要统计信源符号及其出现的概率分布。这通常是一个字典,其中键是符号,值是对应概率。 2. **构造哈夫曼树**:利用频率信息创建一颗哈夫曼树。从频率最高的节点开始,每次合并两个频率最低的节点,直到只剩下一个根节点,即完成哈夫曼树的构建。 3. **生成编码规则**:遍历哈夫曼树,从根到每个叶子节点记录路径。叶子节点的路径就是该节点代表的符号的编码。一般来说,左分支表示0,右分支表示1。 4. **编码和计算**:对于给定的信源序列,根据生成的编码规则进行编码。同时,记录每个符号的总编码长度,用于计算平均码长。 5. **计算效率**:平均码长除以原始信源的平均信息量(通常是每个符号的信息熵),得到编码效率。 以下是简单的MATLAB伪代码示例: ```matlab function [code, avg_code_len] = huffmanCode(freqs) % Step 1: Frequency distribution freqTable = containers.Map(freqs); % Step 2: Build Huffman Tree nodeList = cell(1, length(freqTable)); while numel(nodeList) > 1 [sortedNodes, idx] = sort(freqTable.values); newNode = struct('left', nodeList{idx(1)}, 'right', nodeList{idx(2)}, 'freq', sortedNodes(1)+sortedNodes(2)); nodeList = nodeList(3:end); nodeList(end+1) = newNode; freqTable(newNode.id) = newNode.freq; % Assign new frequency to the merged node end % Step 3: Generate codes and calculate average code length root = nodeList{1}; code = cell(size(freqTable.keys)); for i = 1:numel(freqTable) node = root; code{i} = ''; while ~isempty(node.left) code{i} = strcat(code{i}, '0'); node = node.left; end code{i} = strcat(code{i}, '1'); % Add a 1 to distinguish left and right children end avg_code_len = sum(freqTable.values .* cellfun(@numel, code)) / sum(freqTable.values); % Average code length calculation % Step 4: Return codes and avg code len return code, avg_code_len; end ``` 使用这个函数时,你需要提供一个包含信源符号及其频率的向量作为`freqs`参数,例如 `[1, 1, 2, 3]` 表示第一个符号出现1次,第二个符号也出现1次,第三个符号出现2次,第四个符号出现3次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值