零零散散學算法之詳解數據壓縮算法(下)

深入解析數據壓縮算法

 

前序

      

       開始本文之前,先回顧一下上篇。上篇講解了幾種數據壓縮算法中的兩種:Huffman壓縮算法和RLE壓縮算法。

       詳解數據壓縮算法(上):http://blog.csdn.net/fengchaokobe/article/details/7934865

 

正文

 

       本文將詳解數據壓縮算法的後兩種算法:Rice壓縮算法、LZW壓縮算法。

 

第一節 LZW壓縮算法

 

       LZW壓縮算法:Lempel-Ziv-Welch Encoding。算法的命名簡單直接,故而LZW壓縮算法也跟隨了算法命名的特性:簡單易懂!---有點扯皮了,開始幹正事!

 

       LZW壓縮算法的實現就是通過在編碼的過程中建立一個字符串表,並用某個數字來表示這個串,壓縮文件只存儲那個數字。這些數字就是壓縮後的數據。

 

       LZW算法的實現原理:由於源數據字符串中一定會出現重複的字符串,於是我們就利用這點,將第一次出現的串組合用某一數字表示,然後將這個數字保存起來。那麼當再次遇見這個串時就可直接用這個數字來表示,以此來達到壓縮的目的。所以說,能正確的得到這個數字表是壓縮成功的關鍵。

       從原理中我們可得知:在源數據中,如果重複的字串越多,那麼壓縮的效果就越好。

 

       好了,理論說的再多也是空談,既然瞭解了,那我們就上例子來說明。

       現有如下的源數據串,我們現用LZW壓縮算法來操作實現壓縮:

 

戰前準備:

       1.原理中提到我們用某一數字來表示壓縮的結果,那麼這些數字的起始怎麼選擇呢?這時我們就要根據LZW壓縮算法的應用來說了。LZW主要應用在圖像的處理上,如果圖像的色彩數爲256,那我們就要從258開始(其中256表示清除碼,257表示圖像結束碼)。

 

        2.編碼過程中一些聲明:

           源數據:壓縮的目標數據;

           索引數組:編碼後字符串表所對應數字表的匹配數組。

           匹配結果:Yes | No

          編碼字符表:經過編碼得到的結果。

         數字表標號:當有新的編碼生成時會得到相應的標號。

 

萬事俱備,進入狀態:

       編碼過程中須遵守如下的規則:

編碼的過程:

       從表中我們可知:編碼字符表中的字符和數字就是我們LZW編碼的結果,我們只需將字符串表和數字表一一對應即可,最後我們只保留數字串。

 

       好了,這就是LZW壓縮算法的編碼過程。不過還有一個問題需要解決,就是編碼的標號是從258開始,那麼從多少結束呢?顯然,我們不可能讓其一直增大下去!於是,我們就規定一下:當標號達到4096時(GIF規範規定的是12位,超過12位的表達範圍就得重來),我們就將整個標號集重新初始化,開始使用新的標記,提高了利用率和效率,何樂而不爲呢!

 

       LZW壓縮算法就講完了,算法的實現過程大致的說明白了,不過有些細節還是沒有講到,以後如果遇見了一定會在文中補充!

 

最後一項,LZW編碼算法的核心代碼:

/***這個結構體是爲了用結構體的成員表示編碼串和數字值***/ typedef struct Dictionary{ 	 	int	value;	/**編碼的值**/ 	unsigned char	prefix_string;	/**與prefix作比較**/ 	unsigned char	char_add;	/**與suffix作比較**/ }Dictionary; Dictionary	dict[MaxLength];	/**MaxLength = 4096**/
/***LZW壓縮算法的實現過程***/ int * lzw_coding(unsigned char *src_ch, unsigned int *Prefix_Suffix, int src_length, unsigned int *Char_Stream) { /***	src_ch表示源數據串  ***	Prefix_Suffix表示索引數組  ***	src_length表示源數據串的長度  ***	Char_Stream表示經過編碼後的數字表 ***/ 	int	i = 0; 	int	j = 0; 	int	k = 0; 	int	temp = 0; 	int	code = 258;	//	從258開始  	while(i < src_length) 	{ 		Prefix_Suffix[j+1] = src_ch[i];	//源數據賦值給suffix  		if(Prefix_Suffix[j] == 0) 		{ 			Prefix_Suffix[j] = Prefix_Suffix[j+1]; 			i++; 			continue; 		}  		temp = compare(Prefix_Suffix[j], Prefix_Suffix[j + 1]); 		if(dict[temp].value != UNUSED)	//在串表中可以找到,UNUSED = -1 		{ 			Prefix_Suffix[j] = dict[temp].value;	//如若相同變索引 		} 		else	//串表中找不到,如若不同則編碼 		{ 			dict[temp].value = code++; 			dict[temp].prefix_string = Prefix_Suffix[j]; 			dict[temp].char_add = Prefix_Suffix[j+1];  			Char_Stream[k++] = Prefix_Suffix[j]; 			Prefix_Suffix[j] = Prefix_Suffix[j + 1]; 		} 		i++; 	} 	return Char_Stream; }
/***比較索引數組的串是否在串表中出現過***/ int compare(unsigned char prefix, unsigned char suffix) { 	int	i = 0;  	i = prefix % MaxLength; 	 	while(1) 	{ 		if(dict[i].value == UNUSED) 		{ 			return i; 		}  		if(dict[i].prefix_string = prefix && dict[i].char_add == suffix) 		{ 			return i; 		} 		 		i++; 	} }

參考文獻:http://tech.watchstor.com/management-115343.htm

 

第二節 Rice壓縮算法

 

       Rice編碼:Rice encoding,是由Robert F. Rice發明的這個算法,我們直譯過來就叫它“大米編碼”。 

 

       Rice壓縮算法的基本思想是:用較少的位來表示多個字(或數字),更重要的是,它能區分當前字(或數字)和下一個字(或數字)的位置

 

       有些人稱RICE壓縮算法是靜態的Huffman編碼算法,還是很有道理的。

       附註:Rice壓縮算法一般都是對較小的數字進行操作,因爲數字越小,它需要的位就越少。

 

       Rice壓縮算法的原理:被除數 =除數 * 商 +餘數。經過“大米”壓縮算法壓縮之後的結果就是由除數和餘數組成的。

 

我先詳細的解釋一下這個原理:

       令 S = Q * M + R(Q和R的組合將會是S的壓縮結果),壓縮結果的表示:

       S:爲將要壓縮的數;

      M:是一個常數,M = 2K

       QQ = S >> K

       R:R = S & (M - 1),R用K位表示。

       其中對於K,K表示一個數的位數,而這個K是由一些數的平均係數來決定的。比如現有:10,12,14,18,36這五個數,你會發現前三個數的平均位數爲4,於是這個K就是4了。也就是說,在某一範圍內,一些數的出現次數較多,且這些數可用K位來表示,那麼K就定下來了。

 

       好了,原理說明白了,我們用一個例子實現編碼的過程:

       在“大米”壓縮之前,我先說明一下常規方法,這樣就可以和“大米”壓縮算法作比較了,更加清晰的理解。

 

       常規情況下,我們對上述五個數編碼,即就是:1010 1100 1110 10010 100100。試想想,如果這樣的話,解碼的時候你怎樣區分當前數和下一個數呢?這就比較麻煩了。所以,Rice算法正好解決了這個問題。

 

       現對10,12,14,18,36這五個數用“大米”壓縮算法進行編碼,在原理中已經說明如何得到K,於是K = 4。那麼就有:

       首先,對於10,12,14這三個數可直接用4位表示,即1010,1100,1110。解碼的時候已知K = 4,所以很容易。

 

接下來,對於18,可表示爲:10010,根據編碼原理:

       M = 2K= 16;

       Q = S >> K = 18 >> 4 = 10010 >> 4 = 0b0001;Q轉爲十進制是1,根據表示方法,1的後面跟1個0,即10;

       R = S & (M - 1) = 18 & (16 - 1) = 10010 & 1111 = 0b0010

於是,壓縮結果就是:100010。

 

最後,對於36,表示爲:100100,根據編碼原理:

       M = 2K= 16;

       Q = S >> K = 36 >> 4 = 100100 >> 4 = 0b0010;Q轉爲十進制的值是2,根據表示方法,1的後面跟2個0;

       R = S & (M - 1) = 36 & (M - 1) = 100100 & 1111 = 0b0100

於是壓縮結果就是:1000100。

 

       怎麼樣,挺容易吧,這就是“大米”壓縮算法。該算法的關鍵就是能辨別出當前數和下一個數的位置!解壓縮的過程更簡單了,我們已知K的值和Q與R的規範,直接解壓就OK!

 

好了,最後就是壓縮和解壓縮的算法:

壓縮: char * Rice_coding(char src) { 	if(src & 0xf0 == 0)	//可直接用K位表示,標誌位置0 	{ 		printf 直接輸出K位; 	} 	else	//超過K位,標誌位置1 	{ 		Q = SRC >> K; 		temp_Q = (int)(Q & 0XFF); 		 		R = src & (M - 1); 		 		printf 1 + temp_Q + R; 	} }
解壓縮: char Rice_decoding(char *src) { 	if(標誌位爲0)	//源數據可用K位表示 	{ 		直接取K位還原 	} 	else	//標誌位爲1 	{ 		取Q的值,Q已知(從壓縮過程中獲得); 		R串 = src - Q串; 		 		S = Q × M + R; 	} }


第三節 結束語

 

       想想、寫寫、畫畫......

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章