此篇文章是《程序員須要瞭解的硬核知識》第五篇文章,歷史文章請戳程序員
以前的文章更多的介紹了計算機的硬件知識,會有一些難度,本篇文章的門檻會低一些,一塊兒來看一下計算機中都有哪些壓縮算法
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 便可得到《程序是怎樣跑起來的》電子書