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

深入解析數據壓縮算法

正文

       所謂數據壓縮,是指在不丟失信息的前提下,縮減數據量以減少存儲空間,提高傳輸、存儲和處理效率的一種技術方法。或者是按照一定的算法對數據進行重新組織,減少數據的冗餘和存儲的空間。

 

       能實現數據壓縮的本質原因就是數據的冗餘性。

       本系列將分爲上下兩個部分,介紹四種數據壓縮算法,分別爲Huffman壓縮算法、RLE壓縮算法、LZW壓縮算法、Rice壓縮算法

       其中本文將詳解Huffman壓縮算法和RLE壓縮算法。

 

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

 

第一節 Huffman壓縮算法


       huffman壓縮算法可以說是無損壓縮中最優秀的算法。它使用預先二進制描述來替換每個符號,長度由特殊符號出現的頻率決定。其中出現次數比較多的符號需要很少的位來表示,而出現次數較少的符號則需要較多的位來表示。

 

       huffman壓縮算法的原理:利用數據出現的次數構造Huffman二叉樹,並且出現次數較多的數據在樹的上層,出現次數較少的數據在樹的下層。於是,我們就可以從根節點到每個數據的路徑來進行編碼並實現壓縮。

 

       老辦法,還是舉個例子。

       假設有一個包含100000個字符的數據文件要壓縮存儲。各字符在該文件中的出現頻度如下所示:

       在此,我會給出常規編碼的方法和Huffman編碼兩種方法,這便於我們比較。

       常規編碼方法:我們爲每個字符賦予一個三位的編碼,於是有:


       此時,100000個字符進行編碼需要100000 * 3 = 300000位。


       Huffman編碼:利用字符出現的頻度構造二叉樹,構造二叉樹的過程也就是編碼的過程。

這種情況下,對100000個字符編碼需要:(45 * 1 + (16 + 13 + 12 + 9)*3 + (9 + 5)*4) * 1000 = 224000

 

孰好孰壞,例子說明了一切!好了,老規矩,下面我還是用上面的例子詳細說明一下Huffman編碼的過程。

       首先,我們需要統計出各個字符出現的次數,如下:

       接下來,我根據各個字符出現的次數對它們進行排序,如下:

       好了,一切準備工作就緒。

       在上文我提到,huffman編碼的過程其實就是構造一顆二叉樹的過程,那麼我將各個字符看成樹中將要構造的各個節點,將字符出現的頻度看成權值。Ok,有了這個思想,here we go!

       構造huffman編碼二叉樹規則:

從小到大,

從底向上,

依次排開,

逐步構造。

       首先,根據構造規則,我將各個字符看成構造樹的節點,即有節點a、b、c、d、e、f。那麼,我先將節點f和節點e合併,如下圖:

於是就有:

經過排序處理得:

 

        接下來,將節點b和節點c也合併,則有:

       於是有:

       經過排序處理得:

       第三步,將節點d和節點fe合併,得:

       於是有:

       繼續,這次將節點fed和節點bc合併,得:

       於是有:

       最後,將節點a和節點bcfed合併,有:

       以上步驟就是huffman二叉樹的構造過程,完整的樹如下:

       二叉樹成了,最後就剩下編碼了,編碼的規則爲:01

       於是根據編碼規則得到我們最終想要的結果:

       從上圖中我們得到各個字符編碼後的編碼位:

       Ok,過程清楚了,我們來看看核心代碼:由於Huffman編碼在以前的文章已經給出過,故在這隻給出鏈接!

       Huffman編碼:http://blog.csdn.net/fengchaokobe/article/details/6969217

       好了,Huffman編碼就講完了。Go on!

 

 第二節 RLE壓縮算法

 

       RLE:Run-length Encoding,譯爲“行程長度編碼”,它是一個無損壓縮的非常簡單的算法。

 

       RLE壓縮算法的原理:統計某一節字節在整個字節表中出現的次數,並以該字節和出現的次數作爲編碼的依據。

 

       好了,光說理論解決不了問題,還是用例子來說明。

現有如下一些字節數據:

       那麼,首先我對上述每個數據出現次數做統計,即得到:

       ok,編碼的依據得到了,萬事俱備。由於RLE編碼太簡單了,於是我們一步就得到編碼的結果了。如下:

       編碼的過程簡單,代碼的實現也就很容易了,下面我們看看核心代碼:

char * REL_Coding(char *src_ch, int length, char *dst_ch)
/***	src_ch爲原始字符串,
 ***	length爲原始字符串的長度,
 ***	dst_ch爲根據算法得到的編碼
 ***/
{
        int     i = 0;
        int     j = 0;
        int     count = 0;
        char    p = src_ch[0];

        while(i <= length)	//開始編碼
        {
                if(p == src_ch[i])//如果有重複,計算其重複的次數
                {
                        i++;;
                        count++;
                        continue;
                }
                dst_ch[j] = p;
                dst_ch[++j] = (count + '0');
                j++;
                count = 0;
                p = src_ch[i];	//下一次比較的開始位置
        }

        return dst_ch;
}

       對於上述方法,有人提出:如果字節表中出現連續不重複的數據,就會因爲設置太多的字節次數爲而達不到壓縮的效果。再想的壞一點,如果字符表中的各個字符只出現一次,也就是全部不重複,那麼經過RLE編碼之後,不僅沒有實現壓縮,反倒是增加了一倍。比如“ABCDEFG”,如果用上述方法,那麼經過壓縮後應爲:A1B1C1D1E1F1G1,好壞一目瞭然。當然這是最壞的情況,但是我們必須考慮!那麼此時我們該怎麼辦呢?

 

       針對上述問題,人們對算法做了一些改進。改進算法的核心思想是:我們知道一個字節是八位,那麼用最高位來當做一個標誌位,這個標誌位如果爲1,則表示後面跟的是重複的數據,如果爲0則表示後面跟的是非重複的數據;我們用低七位來表示這個數據重複的次數。用下一個字節來表示這個字節數據。

 

       舉個例子,我們現有一下數據:

對於表中的數據通過前後位的比較,有兩種情況:

       1.如果有幾個連續重複的數據,則最高位置1,並計算重複的次數;

        2.如果當前數據與下一個數據不同,則表示當前數據沒有重複,置0,繼續向後查找。

          注意:當出現連續出現不重複數據的情況時,如“ABCD”,此時只可用一個標誌位來表示。

 

於是,便得到了壓縮後的結果:

       我們來計算比較一下,壓縮之前需要15個字節,而用改進算法壓縮之後只需要11個字節。而且,改進的算法在原始數據越長的情況下,壓縮的效果就越優秀。

       這麼優秀的算法,必須給出核心代碼,如下:

unsigned char * rle_coding(char * src_ch, int length, unsigned char * dst_ch)
/***	src_ch爲原始字符串,
 ***	length爲原始字符串的長度,
 ***	dst_ch爲根據算法得到的編碼
 ***/
{
        int     i = 0;
        int     j = 0;
        int     k = 0;
        int     count = 0;
        char    p = src_ch[0];

        while(i <= length)	//開始編碼
        {
                if(src_ch[i] == p)	//如果有重複,計算其重複的次數
                {
                        count++;
                        i++;
                        continue;
                }

                if(count > 2)	//如果重複的次數滿足條件,則最高位置1
                {
                        dst_ch[j] = dst_ch[j] | 0x80;
                }

                dst_ch[j] = dst_ch[j] | count;	//低位保留重複的次數
                dst_ch[j + 1] = p;
                count = 0;
                p = src_ch[i];	//下一次比較的開始位置
				
				/*
                for(k = 0; k < 0x2; k++)
                {
                        if(j % 2 == 0)
                        {
                                printf("%x", dst_ch[j]);
                                j++;
                        }
                        else
                        {
                                printf("%c", dst_ch[j]);
                                j++;
                        }
                }
				*/
				j++;
        }
        return dst_ch;
}

       好了,RLE壓縮算法就這些,怎麼樣,簡單吧!本文的兩種算法講完了,其餘的壓縮算法會在下篇中跟進!
      

第三節 結束語

       想想、寫寫、畫畫......(未完,接下篇)

 

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