LZ77: 基於重複語句的壓縮
1 什麼是LZ77
1977年兩個以色列人提出的基於重複語句上的通用的壓縮算法--------將重複語句替換成更短的<長度, 距離, 下個字符串首字母>對的方式。 真正的LZ77用的是一個三元組(長度距離對 + 下一個語句的首字母 )
爲什麼叫基LZ77算法的壓縮?
上邊介紹了 原LZ77 的處理方式:
<長度, 距離, 下個字符串首字母> 三字節
我們發現:下個字符串的首字母對壓縮目的來說沒什麼幫助,放大看卻佔了很多空間,因此 我們採用<長度, 距離>的方式
長度距離對 我們仍採用三個字節
長度 :1字節表示
距離 :2字節表示
實現思想:
要壓縮文件,必然需要將待壓縮文件讀到程序的緩衝區中,因此我們需要在程序中維護一段緩衝區
LZ77 :緩衝區的大小 :64K
那麼我們也用64K
我們將緩衝區等分爲兩部分:
查找緩衝區32K:
1 存放已經壓縮完成的數據,
2 待壓縮的數據中的每個字符串(語句)需要在該區域中找是否重複
3 隨着壓縮的不斷進行,查找緩衝區不斷增大
先行緩衝區32K:
1 存放待壓縮的數據
2 每次取出一個字符串(語句)去查找緩衝區中匹配
3 隨着壓縮的不斷進行,先行緩衝區的大小不斷變小
壓縮核心是將重複語句替換爲長度距離對:
那麼重複字符串的長度爲幾的時候進行長度距離對的替換?
1 確定長度:
用1字節表示
首先 :匹配字符串的長度我們用一個字節存儲:範圍【0,255】
爲什麼用一個字節存儲:
1: 一個字節能表示的最大長度已經比較大了,
2: 用更長的長度來存儲時對壓縮率有一定影響,因爲匹配串的距離大部分小於255。
2 確定距離
怎麼確定距離:
距離是先行緩衝區離查找緩衝區的最遠距離
線性緩衝區:32K 查找緩衝區:32K
那麼極端距離爲64K —> 2^16
所以2字節就能表示
1 確定距離受緩衝區的影響:
查找緩衝區越大,能查找到匹配的概率越大,但匹配查找的時間代價也增大了真正查找長度不會超過32K
2。距離再過遠 ,存儲長度就得用更多的字節,又浪費了壓縮空間。
所以真正查找匹配的範圍不會超過32K(一半長度)==>2^3 * K —>2^15 那麼我們用兩個字節來進行保存長度(2^16)
確定壓縮條件
長度爲1個字節的語句 : 不壓縮
長度爲2個字節的語句 : 沒必要(長度距離對是3個字節,那麼肯定不划算)
所以 :
長度 >=3 時開始匹配替換:
Min_Match_Len =3;
Max_Match_Len =258 (利用了0 ,1,2 用不到的空間)
技術纔是硬道理
問題1:如何快速查找匹配?
(查找匹配的時間嚴重影響壓縮時間)
暴力匹配太慢
我們用哈希的思想
哈希 :
首先需要一個哈希表,每次是3個字符進行存儲,哈希表中將來保存的是從查找緩衝區中取到的三個字符中首字符 在查找緩衝區中的下標
問題2 :你可能會想,想要匹配必須先構建哈希彪表 , 構建哈希表肯定得需要遍歷一趟來存儲哈希表中,這不是又影響了時間嗎?
遇到凡事------不要慌~~~
我們讓在哈希表中匹配位置和哈希表的構造進行同步進行就可以了。
什麼意思呢?
邊走邊構造:哈希表中存0那就是該位置沒保存,那麼保存在該位置就行了,哈希表中存1的位置就是存在衝突了,衝突那我們一會再來解決衝突。
需要確定哈希函數
字符串哈希函數我們可以網上查一下
哈希表中匹配位置則需要確定哈希函數,該字符串哈希函數只起將字符串轉爲唯一整形數字的作用,我們可以網上查這個哈希函數
LZ77 哈希函數:
A(4,5) +b(6,7,8)^B(1,2,3) + B(4,5) + B(6,7,8)^C(1,2,3)+ C(4,5,6,7,8)
A B C 代表第一、二、三個位置
12345678 代表字符的比特位
+是連接起來的意思
^優先於+
由哈希函數可以確定 哈希地址最大有15個比特位。
舉個栗子🌰理思路:
🌰:
1 從先行緩衝區中拿到一個字符串abc,
2 通過哈希函數計算在哈希表中的位置
3 到哈希表中取匹配字符串在查找緩衝區的位置
只要三個字符(字符串)相同,那麼計算出的哈希地址一定相同,然後就相當於知道了查找緩衝區的位置,於是就可以確定一個長度距離對了
三個字符的組合數量256* 256* 256 ;
哈希表的大小,因爲組合方式不一樣,哈希函數計算結果必須一一映射
那這樣的話?
哈希表必須有2^24個位置,
每個位置存儲的是在64K窗口中的下標
64K窗口的下標範圍 :【0,2^16】;
每個下標2個字節存儲
那麼就需要兩個字節表示距離.2^24 * 2 ==>32M
**其實查了下資料,還真不是這樣搞的。。。**
因爲32M的哈希表太大,隨着壓縮的進行,哈希表中的內容要不斷更新,更不好維護。
因此 實際LZ77算法哈希表中的位置個數給2^15 —>32K(其實之前的哈希函數可以推出來哈希地址的範圍)
乾貨來了~
32K的哈希表表必然會存在哈希衝突,那麼怎麼解決衝突的?
實際緩衝區:64K=32K + 32K
開闢一個同樣大的Prev作爲處理哈希衝突的空間(32K)
1 爲什麼Prev和Head一樣大?
因爲當匹配暫停時,需要將右窗WSIZE數據導入到左窗中
2 什麼時候匹配會暫停?
匹配暫停發生在線性緩衝區的末尾,當先行緩衝區中剩餘的字符數量不足最大匹配長度時,我們認爲這次匹配不夠完全,因此暫不進行。
怎麼做?
1 將先行緩衝區中的32K數據全部導入到查找緩衝區,相當於更新了查找緩衝區
2 從文件中接着讀取32K(Wsize )的文件
那麼繼續從start位置(上次匹配串進行的位置)開始匹配查找
這樣存在一個小誤區 :你可能會認爲這樣查找緩衝區不足32K了,會不會出錯?
答案是不會的!!
此時查找緩衝區的確是變小了,但倘若在此小區間找不到,那麼久默認此串不存在重複,那麼就把這個新字符串加入到哈希表中就行了,不用向更遠的地方找,損失但性能也是微乎其微的,甚至是有益的!
爲什麼損失性能微乎其微甚至有益?
首先 查找緩衝區變小,那麼查找的更快。
倘若向前接着找的話,也不一定能找到匹配串,這下你明白了嗎?
暫時小結一下:
當前存在問題1: 32K的哈希表必然存在哈希衝突
**當前存在問題2:**對於長度大於64K的文件仍無法進行壓縮
下一篇:我們來展開探究這兩個問題,歡迎持續關注。