日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于Huffman算法和LZ77算法的文件压缩的改进方向

發布時間:2024/4/11 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Huffman算法和LZ77算法的文件压缩的改进方向 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于Huffman算法和LZ77算法的文件壓縮(八)

到這里已經簡單實現基于Huffman算法和LZ77算法的文件壓縮,

GitHub源碼:點我

根據基于Huffman算法和LZ77算法的文件壓縮(七)已經介紹當前項目的缺陷及改進方法。

那么本文只講思想,不實現。

一、范式Huffman樹

范式hu?man樹是在hu?man樹的基礎之上,進行了一些強制性的約定,即:對于同一層節點中,所有的 葉子節點都調整到左邊,然后,對于同一層的葉子節點按照符號順序從小到大調整 ,最后按照左0右1的方式 分配編碼。大家仔細觀察下圖:

從上表中可以得出一些結論:

  • 只要知道一個符號的編碼位長就可以知道它在范式樹上的位置。即:碼表中只要保存每個符號的編碼長 度(即節點在樹中的高度)即可,其遠遠要比符號頻度小
  • 相同位長的編碼之間都相差1
  • 第n層的編碼可以根據上層算出來:code = (code + count[n-1])<<1;


  • 范式hu?man樹該如何創建呢? 范式hu?man樹根本不用創建,可以利用hu?man樹推到出來

  • 對hu?man樹中的每個葉子節點求層數,得出hu?man碼表
  • 對hu?man碼表按照:碼長(節點在樹中的高度)為第一關鍵字、符號為第二關鍵字進行排序
  • 通過以上兩步就可以得出范式hu?man樹的碼表,然后按照上面的公式既可以計算出范式hu?man碼表。

    二、 基于范式hu?man樹的壓縮與解壓縮

    1 壓縮

  • 通過前面介紹的方式計算出hu?man碼表,并推算出每個字符的范式hu?man編碼
  • 讀取源文件,將源文件中的每個字節按照對應的范式hu?man編碼進行改寫



  • 壓縮文件的格式:

  • 先保存各個字節對應的碼字長度(hu?man壓縮中保存的是符號及符號出現的頻率)
  • 保存壓縮數據
  • 2 解壓縮

  • 從壓縮數據中獲取符號的編碼位長,構建符號位長表

  • 2. 根據編碼位長建立解碼表

  • 解碼
  • 注意:范式huffman編碼有一個很重要的特性即長度為i的碼字的前j位的數值大于長度為j的碼字的數值,其中i > j。

    循環進行一下操作,直到所有的比特流解析完成:設i=0

  • 從解碼表的第i行開始,根據編碼位長從壓縮數據比特流中獲取相應長度的比特位。
  • 將讀取的數據與首編碼相減,假設結果為num
  • 如果num>=符號數量,i++,繼續1,如果num小于符號數量,進行4
  • 將符號索引加上num,用該結果從符號位長表對應位置解析出該符號
  • 例如,輸入數據“11110”。令i = 0,此時編碼位長為2。讀取2位的數據“11”與首編碼相減等于3。 3大于等于符號數量,于是i = i + 1等于1。此時編碼位長為3。讀取3位的數據“111”與首編碼相減等于1。 1大于等于符號數量,于是i = i + 1等于2。此時編碼位長為5。讀取5位的數據“11110”與首編碼相減等于2。 2小于符號數量,2加符號索引4等于6。 從表2.3中可以查到序號為6的符號是“E”。從而解碼出符號“E”。跳過當前已經解碼的5位數據,可以重新開始解碼下一個符號。

    4. Huffman壓縮LZ77的結果

    采用huffman樹對LZ77的結果壓縮時,GZIP將原字符和長度放在一起壓縮,因為這兩部分都占一個字節,將距離單獨壓縮。

    4.1 距離的壓縮

    Z77在查找緩沖區中找匹配時,最長的距離不會超過32K,即最大的距離為32768,即距離的范圍是[1,32768],距離會非常多,雖然不會達到32768個,但是如果對于一個比較大的文件進行LZ編碼,distance上千還是很正常的,因此會導致huffman樹非常大,計算量和內存消耗都會超過當時的硬件條件,怎么辦呢?

    GZIP提供了一種非常好的方式,將distance劃分成多個區間,每個區間當做一個整數來看,該整數稱為Distance Code。當一個distance落到某個區間,則相當于出現了那個Code,雖然distance很多,Distance Code可以劃分少一點,即多個distance對應一個Distance Code,最后只需要對Distance Code進行huffman編碼即可。得到Code后,Distance Code再根據一定規則擴展出來。GZIP最終將distance劃分成了30個區間,如下圖:


    Code表示區間編號,[0,29],總共是30個區間,每個區間容納distance的個數剛好是2的n次冪,huffman樹只對0~29這30個Code進行編碼,

    得到編碼,Extra bits表示distance的編碼需要再Code的編碼基礎上擴展的比特位個數,

    比如:0表示不擴展,13表示要擴展13位,因為最大的區間中包含的distance數量為8192個。

    比如:17~24這個區間的huffman編碼為110,因為這個區間有8個整數,于是按照上述表格的規則就可以得到所有distance的編碼:

    17-----> 110 000
    18-----> 110 001
    19-----> 110 010
    20-----> 110 011
    21-----> 110 100
    22-----> 110 101
    23-----> 110 110
    24-----> 110 111

    這樣就可以將樹的高度降低,計算的時間和空間復雜度都降低了,而且擴展起來也比較簡單

    4.2 原字符和長度的壓縮

    原字符表示在LZ77中未匹配的字符,長度表示重復字符串的個數,都占了一個字節,因此GZIP將其壓縮合二為一了,即對于原字符和距離采用同一棵huffman樹進行處理

    原字符的范圍是[0, 255],距離是[3, 258],如何進行處理呢?
    GZIP用整數0~255表示原字符,256表示結束標志,即解碼以后是256表示解碼結束,從257開始表示距離,比如:257表示重復3個字符,258重復4個字符,但GZIP并沒有一直這么一一對應,而是采用了和distance類似的方式進行分區,總共將距離劃分成了29個區間,如下圖

    即原字符和距離的hu?man編碼的輸入元素一共有285個,當解碼器接收到一個比特流的時候,首先可以按 照literal/length這個碼表來解碼,如果解出來是0-255,就表示未匹配字符,如果是256,那自然就結束, 如果是257-285之間,則表示length,把后面擴展比特加上形成length后,后面的比特流肯定就表示 distance

    因此,實際上通過一個Hu?man碼表,對各類情況進行了統一,而不是通過加一個什么標志來 區分到底是literal還是重復字符串。

    到此GZIP的主體壓縮過程基本出來了,第一步:先是采用LZ77對源文件進行壓縮,第二步采用hu?man對 LZ77的壓縮結果進行再次壓縮,因為原字符和長度使用一棵hu?man樹,將其稱為hu?man碼表1, distance對應hu?man樹稱為hu?man碼表2,而最終的hu?man樹信息只需要使用碼字長度保存即可,稱之 為CL(Code Length),即兩個碼表長度分別為:CL1、CL2。

    碼樹記錄下來,對原字符的編碼比特流稱為LIT比 特流,對distance編碼的比特流稱為DIST比特流。按照上面的方法,LZ的編碼結果就變成四塊:CL1、CL2、 LIT比特流、DIST比特流 。

    5. CL的游程編碼

    編碼的長度即CL也是一對數字,該部分信息理論也可以使用huffman樹再次壓縮,但是GZIP并沒有對其使用huffman樹進行壓縮,而是使用了游程編碼。

    游程,即一段完全相同的數的序列。游程編碼,即對一段連續相同的數,記錄這個數一次,緊接著記錄出現了多少個。比如CL序列如下:
    4, 4, 4, 4, 4, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2
    那么,游程編碼的結果為:

    4, 16, 01(二進制), 3, 3, 3, 6, 16, 11(二進制), 16, 00(二進制), 17,011(二進制), 2, 16, 00(二進制)

    這是什么意思呢?因為CL的范圍是0-15,GZIP認為重復出現2次太短就不用游程編碼了,所以游程長度從3開始

    用16這個特殊的數表示重復出現3、4、5、6個這樣一個游程,分別后面跟著00、01、10、11表示(實際存儲的時候需要低比特優先存儲,需要把比特倒序來存,一些例子有時候會忽略這點,實際寫程序的時候一定要注意,否則會得到錯誤結果)。

    于是4,4,4,4,4,這段游程記錄為4,16,01,也就是說,4這個數,后面還會連續出現了4次。6,16,11,16,00表示6后面還連續跟著6個6,再跟著3個6;

    因為連續的0出現的可能很多,所以用17、18這兩個特殊的數專門表示0游程,17后面跟著3個比特分別記錄長度為3-10(總共8種可能)的游程;

    18后面跟著7個比特表示11-138(總共128種可能)的游程。17,011(二進制)表示連續出現6個0;18,0111110(二進制)表示連續出現62個0。

    總之記住,0-15是CL可能出現的值,16表示除了0以外的其它游程;17、18表示0游程。因為二進制實際上也是個整數,所以上面的序列用整數表示為:
    4, 16, 1, 3, 3, 3, 6, 16, 3, 16, 0, 17, 3, 2, 16, 0

    原字符和長度的編碼符號總共有286個(256個原字符+1個結束標記+29個長度區間),distance編碼區間總共30個,因此這棵樹不會特別深,huffman編碼后的碼字長度不會特別長,不會超過15,即樹的深度不會超過15,因此CL1和CL2這兩個序列的任意整數的值的范圍是0-15,0表示沒有出現,故GZIP對CL1和CL2使用了游程編碼。

    因為游程編碼之后整數值的范圍是0-18,這個序列稱之為SQ,因為碼字長度有CL1、CL2,因此最后有SQ1和SQ2兩組數據。GZIP采用第三個huffman樹對SQ1和SQ2再次進行huffman壓縮。

    通過統計各個整數(0-18范圍內)的出現次數,按照相同的思路,對SQ1和SQ2進行了Huffman編碼,得到的碼流記為SQ1 bits和SQ2 bits。同時,這里又需要記錄第三個碼表,稱為Huffman碼表3。同理,這個碼表也用相同的方法記錄,也等效為一個碼長序列,稱為CCL。到此GZIP壓縮才算真正結束,這個算法命名為Deflate算法:

    6. 數據存儲格式

    因為被壓縮的文件可能非常大,會嚴重影響壓縮率,因此GZIP采用了分段壓縮處理,每段的壓縮結果表示如 下:

    總結:

    1. 范式Huffman樹是用來解決Huffman樹當中字符頻度信息、Huffman樹太大的,用范式Huffman樹當中每個字符的位長來代替Huffman樹當中的字符頻度,Huffman樹當中每個字符的頻度是[0,65535],范式Huffman樹的位長是[0,15]。
    如可以采用...000000023456200000...共256個字節這樣的方式來存儲字符位長信息,多次重復出現的數字可以采用CL游程編碼解決。這樣就大大減少了標記信息所占用的空間,使用范式Huffman樹,大大減少了遍歷Huffman樹的時間,提高壓縮的效率。

    2. 對于LZ77壓縮的結果當中,有原字符也有長度距離信息,Huffman的壓縮方法是把標記信息也壓縮到Huffman樹當中,那樣是非常浪費空間的,范式Huffman樹采用將distance劃分成多個區間的方法,也在一定成程度上節省了空間

    3. 范式Huffman樹對原字符和長度的壓縮是采用一起壓縮的方法,因為二者都占一個字節,那么如何區分呢?因為一個字符的范圍是[0,255],所以用來表示字符,用256來分割,[256,285]這29個區間來表示長度

    總結

    以上是生活随笔為你收集整理的基于Huffman算法和LZ77算法的文件压缩的改进方向的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。