認識壓縮算法
我們想必都有過壓縮和解壓縮文件的經歷,當文件太大時,我們會使用文件壓縮來降低文件的占用空間。比如微信上傳文件的限制是100 MB,我這里有個文件夾無法上傳,但是我解壓完成后的文件定會小於100 MB,那么我的文件就可以上傳了。
此外,我們把相機拍完的照片保存到計算機上的時候,也會使用壓縮算法進行文件壓縮,文件壓縮的格式一般是JPEG。
文件存儲
文件是將數據存儲在磁盤等存儲媒介的一種形式。程序文件中最基本的存儲數據單位是字節。文件的大小不管是xxxKB. xxMB等來表示,就是因為文件是以字節B= Byte為單位來存儲的.
文件就是字節數據的集合。用1字節(8位)表示的字節數據有256種,用二進制表示的話就是0000 0000 - 1111 1111。如果文件中存儲的數據是文字,那么該文件就是文本文件。如果是圖形,那么該文件就是圖像文件。在任何情況下,文件中的字節數都是連續存儲的。
壓縮算法的定義
無損壓縮:
能夠無失真地從壓縮后的數據重構,准確地還原原始數據。可用於對數據的准確性要求嚴格的場合,如可執行文件和普通文件的壓縮、磁盤的壓縮,也可用於多媒體數據的壓縮。該方法的壓縮比較小。如差分編碼、RLE, Hufftman編碼、LZW編碼、算術編碼
有損壓縮:有失真,不能完全准確地恢復原始數據,重構的數據只是原始數據的一個近似。可用於對數據的准確性要求不高的場合,如多媒體數據的壓縮。該方法的壓縮比較大。例如預測編碼、音感編碼分形壓縮、小波壓縮、JPEG/MPEG
對稱性
如果編解碼算法的復雜性和所需時間差不多,則為對稱的編碼方法,多數壓縮算法都是對稱的。但也有不對稱的,一般是編碼難而解碼容易,如Huffman編碼和分形編碼。但用於密碼學的編碼方法則相反,是編碼容易,而解碼則非常難
幀間與幀內
在視頻編碼中會同時用到幀內與幀間的編碼方法,幀內編碼是指在一幀圖像內獨立完成的編碼方法,同靜態圖像的編碼,如JPEG;而幀間編碼則需要參照前后幀才能進行編解碼,並在編碼過程中考慮對暢之間的時間冗余的壓縮,如MPEG
幾種壓縮算法
RLE算法的機制
接下來就讓我們正式看一下文件的壓縮機制。首先讓我們來嘗試對 AAAABBCDEEEEF這17個半角字符的文件(文本文件)進行壓縮。雖然這些文字沒有什么實際意義,但是很適合用來描述RLE的壓縮機制。
由於半角字符(其實就是英文字符)是作為1個字節保存在文件中的,所以上述的文件的大小就是17字節。
那么,如何才能壓縮該文件呢?大家不妨也考慮一下,只要是能夠使文件小於17字節,我們可以使用任何壓縮算法。
最顯而易見的一種壓縮方式我覺得你已經想到了,就是把相同的字符去重化,也就是字符*重復次數的方式進行壓縮。所以上面文件壓縮后就會變成下面這樣
從圖中我可以看出, AAAAAABBCDDEEEEEF17個字符成功被壓成了A6B2C1D2E5F1的12個字符,也就是12/ 17=70%;壓縮比為70%,壓縮成功了
像這樣,把文件內容用數據*重復次數的形式來表示的壓縮方法成為RLE(Run LengthEncoding,行程長度編碼)算法。RLE算法是一種很好的壓縮方法,經常用於壓縮傳真的圖像等。因為圖像文件的本質也是字節數據的集合體,所以可以用RLE算法進行壓縮
RLE算法的缺點
RLE的壓縮機制比較簡單,所以RLE算法的程序也比較容易編寫,所以使用RLE的這種方式更能讓你體會到壓縮思想,但是RLE只針對特定序列的數據管用,下面是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次+D2次+E"5次+F*1次+字符間隔"16=4位"6次+8位*2次+9位*1次+6位 2次+1位"5次+8位"1次+21* 16次=106位=14字節。
所以使用莫爾斯電碼的壓縮比為14 / 17=82%。效率並不太突出。
用二叉樹實現哈夫曼算法
剛才已經提到,莫爾斯編碼是根據日常文本中各字符的出現頻率來決定表示各字符的編碼數據長度的。不過,在該編碼體系中,對AAAAAABBCDDEEEEEF這種文本來說並不是效率最高的。
下面我們來看一下哈夫曼算法。哈夫曼算法是指,為各壓縮對象文件分別構造最佳的編碼體系,並以該編碼體系為基礎來進行壓縮。因此,用什么樣的編碼(哈夫曼編碼)對數據進行分割,就要由各個文件而定。用哈夫曼算法壓縮過的文件中,存儲着哈夫曼編碼信息和壓縮過的數據。
接下來,我們在對AAABBCDDEEEEEF中的A-F這些字符,按照出現頻率高的字符用盡量少的位數編碼來表示這一原則進行整理。按照出現頻率從高到低的順序整理后,結果如下,同時也列出了編碼方案。
在上表的編碼方案中,隨着出現頻率的降低,字符編碼信息的數據位數也在逐漸增加,從最開始的1位、2位依次增加到3位。不1、0、0這三個編碼來表示E、A、A呢?還是用10、0來表示B、A呢?還是用100來表示C呢。而在哈夫曼算法中,通過借助哈夫曼樹的構造編碼體系,即使在不使用字符區分符號的情況下,也可以構建能夠明確進行區分的編碼體系。不過哈夫曼樹的算法要比較復雜,下面是一個哈夫曼樹的構造過程。