壓縮演算法
認識壓縮演算法
我們想必都有過壓縮和解壓縮檔案的經歷,當檔案太大時,我們會使用檔案壓縮來降低檔案的佔用空間。比如微信上傳檔案的限制是100MB,我這裡有個資料夾無法上傳,但是我解壓完成後的檔案定會小於100MB,那麼我的檔案就可以上傳了。
此外,我們把相機拍完的照片儲存到計算機上的時候,也會使用壓縮演算法進行檔案壓縮,檔案壓縮的格式一般是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(RunLengthEncoding,行程長度編碼)演算法。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呢。而在哈夫曼演算法中,通過藉助哈夫曼樹的構造編碼體系,即使在不使用字元區分符號的情況下,也可以構建能夠明確進行區分的編碼體系。不過哈夫曼樹的演算法要比較複雜,下面是一個哈夫曼樹的構造過程。