此篇文章是《程序员须要了解的硬核知识》第五篇文章,历史文章请戳程序员
以前的文章更多的介绍了计算机的硬件知识,会有一些难度,本篇文章的门槛会低一些,一块儿来看一下计算机中都有哪些压缩算法
spa
咱们想必都有过压缩
和 解压缩
文件的经历,当文件太大时,咱们会使用文件压缩来下降文件的占用空间。好比微信上传文件的限制是100 MB,我这里有个文件夹没法上传,可是我解压完成后的文件必定会小于 100 MB,那么个人文件就能够上传了。3d
此外,咱们把相机拍完的照片保存到计算机上的时候,也会使用压缩算法进行文件压缩,文件压缩的格式通常是JPEG
。code
那么什么是压缩算法呢?压缩算法又是怎么定义的呢?在认识算法以前咱们须要先了解一下文件是如何存储的视频
文件是将数据存储在磁盘等存储媒介的一种形式。程序文件中最基本的存储数据单位是字节
。文件的大小无论是 xxxKB、xxxMB等来表示,就是由于文件是以字节 B = Byte
为单位来存储的。对象
文件就是字节数据的集合。用 1 字节(8 位)表示的字节数据有 256 种,用二进制表示的话就是 0000 0000 - 1111 1111 。若是文件中存储的数据是文字,那么该文件就是文本文件。若是是图形,那么该文件就是图像文件。在任何状况下,文件中的字节数都是连续存储
的。
上面介绍了文件的集合体其实就是一堆字节数据的集合,那么咱们就能够来给压缩算法下一个定义。
压缩算法(compaction algorithm)
指的就是数据压缩的算法,主要包括压缩和还原(解压缩)的两个步骤。
其实就是在不改变原有文件属性的前提下,下降文件字节空间和占用空间的一种算法。
根据压缩算法的定义,咱们可将其分红不一样的类型:
有损和无损
无损压缩:可以无失真地
从压缩后的数据重构,准确地还原原始数据。可用于对数据的准确性要求严格的场合,如可执行文件和普通文件的压缩、磁盘的压缩,也可用于多媒体数据的压缩。该方法的压缩比较小。如差分编码、RLE、Huffman编码、LZW编码、算术编码。
有损压缩:有失真,不能彻底准确地
恢复原始数据,重构的数据只是原始数据的一个近似。可用于对数据的准确性要求不高的场合,如多媒体数据的压缩。该方法的压缩比较大。例如预测编码、音感编码、分形压缩、小波压缩、JPEG/MPEG。
对称性
若是编解码算法的复杂性和所需时间差很少,则为对称的编码方法,多数压缩算法都是对称的。但也有不对称的,通常是编码难而解码容易,如 Huffman 编码和分形编码。但用于密码学的编码方法则相反,是编码容易,而解码则很是难。
帧间与帧内
在视频编码中会同时用到帧内与帧间的编码方法,帧内编码是指在一帧图像内独立完成的编码方法,同静态图像的编码,如 JPEG;而帧间编码则须要参照先后帧才能进行编解码,并在编码过程当中考虑对帧之间的时间冗余的压缩,如 MPEG。
实时性
在有些多媒体的应用场合,须要实时处理或传输数据(如现场的数字录音和录影、播放MP3/RM/VCD/DVD、视频/音频点播、网络现场直播、可视电话、视频会议),编解码通常要求延时 ≤50 ms。这就须要简单/快速/高效的算法和高速/复杂的CPU/DSP芯片。
分级处理
有些压缩算法能够同时处理不一样分辨率、不一样传输速率、不一样质量水平的多媒体数据,如JPEG2000、MPEG-2/4。
这些概念有些抽象,主要是为了让你们了解一下压缩算法的分类,下面咱们就对具体的几种经常使用的压缩算法来分析一下它的特色和优劣
接下来就让咱们正式看一下文件的压缩机制。首先让咱们来尝试对 AAAAAABBCDDEEEEEF
这 17 个半角字符的文件(文本文件)进行压缩。虽然这些文字没有什么实际意义,可是很适合用来描述 RLE
的压缩机制。
因为半角字符(其实就是英文字符)是做为 1 个字节保存在文件中的,因此上述的文件的大小就是 17 字节。如图
(这里有个问题须要读者思考一下:为何 17 个字符的大小是 17 字节,而占用空间却很大呢? 这个问题此篇文章暂不讨论)
那么,如何才能压缩该文件呢?你们不妨也考虑一下,只要是可以使文件小于 17 字节,咱们可使用任何压缩算法。
最显而易见的一种压缩方式我以为你已经想到了,就是把相同的字符去重化
,也就是 字符 * 重复次数
的方式进行压缩。因此上面文件压缩后就会变成下面这样
从图中咱们能够看出,AAAAAABBCDDEEEEEF 的17个字符成功被压缩成了 A6B2C1D2E5F1 的12个字符,也就是 12 / 17 = 70%,压缩比为 70%,压缩成功了。
像这样,把文件内容用 数据 * 重复次数
的形式来表示的压缩方法成为 RLE(Run Length Encoding, 行程长度编码)
算法。RLE 算法是一种很好的压缩方法,常常用于压缩传真的图像等。由于图像文件的本质也是字节数据的集合体,因此能够用 RLE 算法进行压缩
RLE 的压缩机制比较简单,因此 RLE 算法的程序也比较容易编写,因此使用 RLE 的这种方式更能让你体会到压缩思想,可是 RLE 只针对特定序列的数据管用,下面是 RLE 算法压缩汇总
文件类型 | 压缩前文件大小 | 压缩后文件大小 | 压缩比率 |
---|---|---|---|
文本文件 | 14862字节 | 29065字节 | 199% |
图像文件 | 96062字节 | 38328字节 | 40% |
EXE文件 | 24576字节 | 15198字节 | 62% |
经过上表能够看出,使用 RLE 对文本文件进行压缩后的数据不但没有减少反而增大了!几乎是压缩前的两倍!由于文本字符种连续的字符并很少见。
就像上面咱们探讨的这样,RLE 算法只针对连续
的字节序列压缩效果比较好,假若有一连串不相同的字符该怎么压缩呢?好比说ABCDEFGHIJKLMNOPQRSTUVWXYZ
,26个英文字母所占空间应该是 26 个字节,咱们用 RLE 压缩算法压缩后的结果为 A1B1C1D1E1F1G1H1I1J1K1L1M1N1O1P1Q1R1S1T1U1V1W1X1Y1Z1
,所占用 52 个字节,压缩完成后的容量没有减小反而增大了!这显然不是咱们想要的结果,因此这种状况下就不能再使用 RLE 进行压缩。
下面咱们来介绍另一种压缩算法,即哈夫曼算法。在了解哈夫曼算法以前,你必须舍弃半角英文数字的1个字符是1个字节(8位)的数据
。下面咱们就来认识一下哈夫曼算法的基本思想。
文本文件是由不一样类型的字符组合而成的,并且不一样字符出现的次数也是不同的。例如,在某个文本文件中,A 出现了 100次左右,Q仅仅用到了 3 次,相似这样的状况很常见。哈夫曼算法的关键就在于 屡次出现的数据用小于 8 位的字节数表示,不经常使用的数据则可使用超过 8 位的字节数表示。A 和 Q 都用 8 位来表示时,原文件的大小就是 100次 8 位 + 3次 8 位 = 824位,假设 A 用 2 位,Q 用 10 位来表示就是 2 100 + 3 10 = 230 位。
不过要注意一点,最终磁盘的存储都是以8位为一个字节来保存文件的。
哈夫曼算法比较复杂,在深刻了解以前咱们先吃点甜品
,了解一下 莫尔斯编码
,你必定看过美剧或者战争片的电影,在战争中的通讯常常采用莫尔斯编码来传递信息,例以下面
接下来咱们来说解一下莫尔斯编码,下面是莫尔斯编码的示例
,你们把 1 看做是短点(嘀),把 11 看做是长点(嗒)便可。
莫尔斯编码通常把文本中出现最高频率的字符用短编码
来表示。如表所示,假如表示短点的位是 1,表示长点的位是 11 的话,那么 E(嘀)这一数据的字符就能够用 1 来表示,C(滴答滴答)就能够用 9 位的 110101101
来表示。在实际的莫尔斯编码中,若是短点的长度是 1 ,长点的长度就是 3,短点和长点的间隔就是1。这里的长度指的就是声音的长度。好比咱们想用上面的 AAAAAABBCDDEEEEEF 例子来用莫尔斯编码重写,在莫尔斯曼编码中,各个字符之间须要加入表示时间间隔的符号。这里咱们用 00 加以区分。
因此,AAAAAABBCDDEEEEEF 这个文本就变为了 A 6 次 + B 2次 + C 1次 + D 2次 + E 5次 + F 1次 + 字符间隔 16 = 4 位 6次 + 8 位 2次 + 9 位 1 次 + 6位 2次 + 1位 5次 + 8 位 1次 + 2位 16次 = 106位 = 14字节。
因此使用莫尔斯电码的压缩比为 14 / 17 = 82%。效率并不太突出。
刚才已经提到,莫尔斯编码是根据平常文本中各字符的出现频率来决定表示各字符的编码数据长度的。不过,在该编码体系中,对 AAAAAABBCDDEEEEEF 这种文原本说并非效率最高的。
下面咱们来看一下哈夫曼算法。哈夫曼算法是指,为各压缩对象文件分别构造最佳的编码体系,并以该编码体系为基础来进行压缩。所以,用什么样的编码(哈夫曼编码)对数据进行分割,就要由各个文件而定。用哈夫曼算法压缩过的文件中,存储着哈夫曼编码信息和压缩过的数据。
接下来,咱们在对 AAAAAABBCDDEEEEEF 中的 A - F 这些字符,按照出现频率高的字符用尽可能少的位数编码来表示
这一原则进行整理。按照出现频率从高到低的顺序整理后,结果以下,同时也列出了编码方案。
字符 | 出现频率 | 编码(方案) | 位数 |
---|---|---|---|
A | 6 | 0 | 1 |
E | 5 | 1 | 1 |
B | 2 | 10 | 2 |
D | 2 | 11 | 2 |
C | 1 | 100 | 3 |
F | 1 | 101 | 3 |
在上表的编码方案中,随着出现频率的下降,字符编码信息的数据位数也在逐渐增长,从最开始的 1位、2位依次增长到3位。不过这个编码体系是存在问题的,你不知道100这个3位的编码,它的意思是用 一、0、0这三个编码来表示 E、A、A 呢?仍是用十、0来表示 B、A 呢?仍是用100来表示 C 呢。
而在哈夫曼算法中,经过借助哈夫曼树的构造编码体系,即便在不使用字符区分符号的状况下,也能够构建可以明确进行区分的编码体系。不过哈夫曼树的算法要比较复杂,下面是一个哈夫曼树的构造过程。
天然界树的从根开始生叶的,而哈夫曼树则是叶生枝
使用哈夫曼树以后,出现频率越高的数据所占用的位数越少,这也是哈夫曼树的核心思想。经过上图的步骤二能够看出,枝条链接数据时,咱们是从出现频率较低的数据开始的。这就意味着出现频率低的数据到达根部的枝条也越多。而枝条越多则意味着编码的位数随之增长。
接下来咱们来看一下哈夫曼树的压缩比率,用上图获得的数据表示 AAAAAABBCDDEEEEEF 为 000000000000 100100 110 101101 0101010101 111,40位 = 5 字节。压缩前的数据是 17 字节,压缩后的数据居然达到了惊人的5 字节,也就是压缩比率 = 5 / 17 = 29% 如此高的压缩率,简直是太惊艳了。
你们能够参考一下,不管哪一种类型的数据,均可以用哈夫曼树做为压缩算法
文件类型 | 压缩前 | 压缩后 | 压缩比率 |
---|---|---|---|
文本文件 | 14862字节 | 4119字节 | 28% |
图像文件 | 96062字节 | 9456字节 | 10% |
EXE文件 | 24576字节 | 4652字节 | 19% |
最后,咱们来看一下图像文件的数据形式。图像文件的使用目的一般是把图像数据输出到显示器、打印机等设备上。经常使用的图像格式有 : BMP
、JPEG
、TIFF
、GIF
格式等。
图像文件可使用前面介绍的 RLE 算法和哈夫曼算法,由于图像文件在多数状况下并不要求数据须要还原到和压缩以前一摸同样的状态,容许丢失一部分数据。咱们把能还原到压缩前状态的压缩称为 可逆压缩
,没法还原到压缩前状态的压缩称为非可逆压缩
。
通常来讲,JPEG格式的文件是非可逆压缩,所以还原后有部分图像信息比较模糊。GIF 是可逆压缩
文章参考:
《程序是怎样跑起来的 第六章》
关注公众号 Java建设者 后台回复 191106 便可得到《程序是怎样跑起来的》电子书