本篇将介绍 哈夫曼压缩算法(Huffman compression)
众所周知,计算机存储数据时,实际上存储的是一堆0和1(二进制)。
如果我们存储一段字符:ABRACADABRA!
那么计算机会把它们逐一翻译成二进制,如A:01000001;B: 01000010; !: 00001010.
每个字符占8个bits, 这一整段字符则至少占12*8=96 bits。
但如果我们用一些特殊的值来代表这些字符,如:
图中,0代表A; 1111代表B;等等。此时,存储这段字符只需30bits,比96bits小多了,达到了压缩的目的。
我们需要这么一个表格来把原数据翻译成特别的、占空间较少的数据。同时,我们也可以用这个表格,把特别的数据还原成原数据。
首先,为了避免翻译歧义,这个表格需满足一个条件: 任何一个字符用的值都不能是其它字符的前缀 。
我们举个反例:A: 0; B: 01;这里,A的值是B的值的前缀。如果压缩后的数据为01xxxxxx,x为0或者1,那么这个数据应该翻译成A1xxxxxx, 还是Bxxxxxxx?这样就会造成歧义。
然后,不同的表格会有不同的压缩效果,如:
这个表格的压缩效果更好。
那么我们如何找到 最好的表格 呢?这个我们稍后再讲。
为了方便阅读,这个表格是可以写成一棵树的:
这棵树的节点左边是0,右边是1。任何含有字符的节点都没有非空子节点。(即上文提及的前缀问题。)
这棵树是在压缩的过程中建成的,这个表格是在树形成后建成的。用这个表格,我们可以很简单地把一段字符变成压缩后的数据,如:
原数据:ABRACADABRA!
表格如上图。
令压缩后的数据为S;
第一个字符是A,根据表格,A:11,故S=11;
第二个字符是B,根据表格,B:00,故S=1100;
第三个字符是R,根据表格,R:011,故S=1100011;
如此类推,读完所有字符为止。
压缩搞定了,那解压呢?很简单,跟着这棵树读就行了:
压缩后的数据S=11000111101011100110001111101
记住,读到1时,往右走,读到0时,往左走。
令解压后的字符串为D;
从根节点出发,第一个数是1,往右走:
第二个数是1,往右走:
读到有字符的节点,返回此字符,加到字符串D里。D:A;
返回根节点,继续读。
第三个数是0,往左走:
第四个数是0,往左走:
读到有字符的节点,返回此字符,加到字符串D里。D:AB;
返回根节点,继续读。
第五个数是0,往左走:
第六个数是1,往右走:
第七个数是1,往右走:
读到有字符的节点,返回此字符,加到字符串D里。D:ABR;
返回根节点,继续读。
如此类推,直到读完所有压缩后的数据S为止。
压缩与解压都搞定了之后 我们需要先把原数据读一遍,并把每个字符出现的次数记录下来。如:
ABRACADABRA!中,A出现了5次;B出现了2次;C出现了1次;D出现了1次;R出现了2次;!出现了1次。
理论上,出现频率越高的字符,我们给它一个占用空间越小的值,这样,我们就可以有最佳的压缩率
由于哈夫曼压缩算法这块涉及内容较多 ,文章篇幅很长;全文全方面讲解了Compose布局的各方面知识。更多Android前言技术进阶,我自荐一套《 完整的Android的资料,以及一些视频课讲解 》 现在私信发送“进阶”或者“笔记”即可免费获取
最后我想说:
对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
Ⅱ 哈夫曼编码怎么算
哈夫曼编码的算法就是把两个最小的概率相加。
哈夫曼编码,又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码的一种。
Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码。
算法:先按出现的概率大小排队,把两个最小的概率相加,作为新的概率和剩余的概率重新排队,再把最小的两个概率相加,再重新排队,直到最后变成1。每次相加时都将0和1赋与相加的两个概率,读出时由该符号开始一直走到最后的1,将路线上所遇到的0和1按最低位到最高位的顺序排好,就是该符号的赫夫曼编码。
动态哈夫曼编码
Faller等人提出了动态哈夫曼编码方法,它对数据编码的依据是动态变化的哈夫曼树,也就是说,对第t+1个字符编码是根据原始数据中前t个字符得到的哈夫曼树来进行的。
压缩和解压子程序具有相同的初始化树,每处理完一个字符,压缩和解压方使用相同的算法修改哈夫曼树,因而该方法不需要为解压而保存树的有关信息。压缩和解压一个字符所需的时间与该字符的编码长度成正比,因而该过程可以实时进行。
第一步我们把前t个字符的哈夫曼树转换成它的另一种形式,在该树中只需在第二步中简单地把由根到叶结点alol路径上的所有结点重量加1,就可以变成前t+1个字符的哈夫曼树。
以上内容参考:网络—哈夫曼编码
Ⅲ 压缩算法原理
哈夫曼
哈夫曼编码是无损压缩当中最好的方法。它使用预先二进制描述来替换每个符号,长度由特殊符号出现的频率决定。常见的符号需要很少的位来表示,而不常见的符号需要很多为来表示。
哈夫曼算法在改变任何符号二进制编码引起少量密集表现方面是最佳的。然而,它并不处理符号的顺序和重复或序号的序列。
2.1 原理
我不打算探究哈夫曼编码的所有实际的细节,但基本的原理是为每个符号找到新的二进制表示,从而通常符号使用很少的位,不常见的符号使用较多的位。
简短的说,这个问题的解决方案是为了查找每个符号的通用程度,我们建立一个未压缩数据的柱状图;通过递归拆分这个柱状图为两部分来创建一个二叉树,每个递归的一半应该和另一半具有同样的权(权是 ∑ N K =1 符号数 k , N 是分之中符号的数量,符号数 k 是符号 k出现的次数 )
这棵树有两个目的:
1. 编码器使用这棵树来找到每个符号最优的表示方法
2. 解码器使用这棵树唯一的标识在压缩流中每个编码的开始和结束,其通过在读压缩数据位的时候自顶向底的遍历树,选择基于数据流中的每个独立位的分支,一旦一个到达叶子节点,解码器知道一个完整的编码已经读出来了。
压缩后的数据流是 24 位(三个字节),原来是 80 位( 10 个字节)。当然,我应该存储哈夫曼树,这样解码器就能够解码出对应的压缩流了,这就使得该例子中的真正数据流比输入的流数据量大。这是相对较短的数据上的副作用。对于大数据量来说,上面的哈夫曼树就不占太多比例了。
解码的时候,从上到下遍历树,为压缩的流选择从左 / 右分支,每次碰到一个叶子节点的时候,就可以将对应的字节写到解压输出流中,然后再从根开始遍历。
2.2 实现
哈夫曼编码器可以在基本压缩库中找到,其是非常直接的实现。
这个实现的基本缺陷是:
1. 慢位流实现
2. 相当慢的解码(比编码慢)
3. 最大的树深度是 32 (编码器在任何超过 32 位大小的时候退出)。如果我不是搞错的话,这是不可能的,除非输出的数据大于 2 32字节。
另一方面,这个实现有几个优点:
1. 哈夫曼树以一个紧密的形式每个符号要求 12 位(对于 8 位的符号)的方式存储,这意味着最大的头为 384 。
2. 编码相当容易理解
哈夫曼编码在数据有噪音的情况(不是有规律的,例如 RLE )下非常好,这中情况下大多数基于字典方式的编码器都有问题。
Ⅳ 无损压缩算法是什么样的
WinRAR是采用它自己的独创的压缩算法。
【希望你能看看最优二叉树(哈夫曼树),理解哈夫曼编码的原理,对你的这个压缩算法会有很明晰的指导和解惑作用】WinRAR是采用它自己的独创的压缩算法。
压缩处理都是以二进制的方式进行的。这和你的编码有关。只要是处理后的结果比原文档文件小,而且是可逆的还原,就是无压缩。
压缩率的大小和你的编码方式有关。
无损压缩是指重构压缩数据(还原,解压缩),而重构数据与原来数据完全相同。该方法用于那些要求重构信号与原始信号完全一致的场合,如文本数据、程序和特殊应用场合的图像数据(如指纹图像、医学图像等)的压缩。这类算法压缩率较低,一般为1/2~1/5。典型的无损压缩算法有:Shanno-Fano编码、Huffman(哈夫曼)编码、算术编码、游程编码、LZW编码等。
基于哈夫曼编码原理的压缩算法:
哈夫曼算法的过程为:统计原始数据中各字符出现的频率;所有字符按频率降序排列;
比如有一个字符串:aaaaaaaaaabbbbbbcccd
原文件大小存储需要20个字节。如果按频率出现的次数高低,给予字符串中的每个字符不同的编码长度,就可以达到压缩的目的。
如
a编码为01(占用2个bit)
b编码为00(占用2个bit)
c编码为000,(占用3个bit)
c编码为001,(占用3个bit)
那就压缩后的总长为(2*10+2*6+3*3+1*3)/8 =5.5个字节。
另外在解码的时候,要告之对方你的编码方式,需要把编码的规则传递过去。
如果对于以上字符串,你也可以按aaaaaaaaaa编码成一个1,bbbbbb为2,ccc为3,d为4。这样压缩后的内容为最小,但是要注意一点,这时你的编码规则为最大,你要把你的编码规则发给对方的时候,有可能编编解码规则文件可能会比压缩后的内容还要大。最终结果为造成压缩后的文件比原文件还要大。