1、香農編碼
這個例子展示了一組字母的香濃編碼結構(如圖a所示)這五個可被編碼的字母有如下出現次數:
Symbol |
A |
B |
C |
D |
E |
Count |
15 |
7 |
6 |
6 |
5 |
Probabilities |
0.38461538 |
0.17948718 |
0.15384615 |
0.15384615 |
0.12820513 |
從左到右,所有的符號以它們出現的次數劃分。在字母B與C之間劃定分割線,得到了左右兩組,總次數分別爲22,17。 這樣就把兩組的差別降到最小。通過這樣的分割, A與B同時擁有了一個以0爲開頭的碼字, C,D,E的碼子則爲1,如圖b所示。 隨後, 在樹的左半邊,於A,B間建立新的分割線,這樣A就成爲了碼字爲00的葉子節點,B的碼子01。經過四次分割, 得到了一個樹形編碼。 如下表所示,在最終得到的樹中, 擁有最大頻率的符號被兩位編碼, 其他兩個頻率較低的符號被三位編碼。
符號 |
A |
B |
C |
D |
E |
編碼 |
00 |
01 |
10 |
110 |
111 |
2、哈夫曼編碼
3、算術編碼(理解即可)
算術編碼的基本原理是將編碼的消息表示成實數0和1之間的一個間隔(Interval),消息越長,編碼表示它的間隔就越小,表示這一間隔所需的二進制位就越多。
算術編碼用到兩個基本的參數:符號的概率和它的編碼間隔。信源符號的概率決定壓縮編碼的效率,也決定編碼過程中信源符號的間隔,而這些間隔包含在0到1之間。編碼過程中的間隔決定了符號壓縮後的輸出。
給定事件序列的算術編碼步驟如下:
(1)編碼器在開始時將“當前間隔” [ L, H) 設置爲[0,1)。
(2)對每一事件,編碼器按步驟(a)和(b)進行處理
(a)編碼器將“當前間隔”分爲子間隔,每一個事件一個。
(b)一個子間隔的大小與下一個將出現的事件的概率成比例,編碼器選擇子間隔對應於下一個確切發生的事件相對應,並使它成爲新的“當前間隔”。
(3)最後輸出的“當前間隔”的下邊界就是該給定事件序列的算術編碼。
例1:假設信源符號爲{A, B, C, D},這些符號的概率分別爲{ 0.1, 0.4, 0.2,0.3 },根據這些概率可把間隔[0, 1]分成4個子間隔:[0, 0.1], [0.1, 0.5], [0.5, 0.7], [0.7, 1],其中[x,y]表示半開放間隔,即包含x不包含y。上面的信息可綜合在表03-04-1中。
表03-04-1 信源符號,概率和初始編碼間隔
符號 |
A |
B |
C |
D |
概率 |
0.1 |
0.4 |
0.2 |
0.3 |
初始編碼間隔 |
[0, 0.1) |
[0.1, 0.5) |
[0.5, 0.7) |
[0.7, 1] |
如果二進制消息序列的輸入爲:C A D A C D B。編碼時首先輸入的符號是C,找到它的編碼範圍是[0.5,0.7]。由於消息中第二個符號A的編碼範圍是[0, 0.1],因此它的間隔就取[0.5, 0.7]的第一個十分之一作爲新間隔[0.5,0.52]。依此類推,編碼第3個符號D時取新間隔爲[0.514, 0.52],編碼第4個符號A時,取新間隔爲[0.514, 0.5146],…。消息的編碼輸出可以是最後一個間隔中的任意數。整個編碼過程如圖03-04-1所示。
圖03-04-1 算術編碼過程舉例
這個例子的編碼和譯碼的全過程分別表示在表03-04-2和表03-04-3中。
表03-04-2 編碼過程
步驟 |
輸入符號 |
編碼間隔 |
編碼判決 |
1 |
C |
[0.5, 0.7] |
符號的間隔範圍[0.5, 0.7] |
2 |
A |
[0.5, 0.52] |
[0.5, 0.7]間隔的第一個1/10 |
3 |
D |
[0.514, 0.52] |
[0.5, 0.52]間隔的最後一個1/10 |
4 |
A |
[0.514, 0.5146] |
[0.514, 0.52]間隔的第一個1/10 |
5 |
C |
[0.5143,0.51442] |
[0.514, 0.5146]間隔的第五個1/10開始,二個1/10 |
6 |
D |
[0.514384,0.51442] |
[0.5143, 0.51442]間隔的最後3個1/10 |
7 |
B |
[0.5143836,0.514402] |
[0.514384,0.51442]間隔的4個1/10,從第1個1/10開始 |
8 |
從[0.5143876, 0.514402]中選擇一個數作爲輸出:0.5143876 |
表03-04-3 譯碼過程
步驟 |
間隔 |
譯碼符號 |
譯碼判決 |
1 |
[0.5, 0.7] |
C |
0.51439在間隔 [0.5, 0.7) |
2 |
[0.5,0.52] |
A |
0.51439在間隔 [0.5, 0.7)的第1個1/10 |
3 |
[0.514,0.52] |
D |
0.51439在間隔[0.5, 0.52)的第7個1/10 |
4 |
[0.514,0.5146] |
A |
0.51439在間隔[0.514, 0.52]的第1個1/10 |
5 |
[0.5143,0.51442] |
C |
0.51439在間隔[0.514, 0.5146]的第5個1/10 |
6 |
[0.514384,0.51442] |
D |
0.51439在間隔[0.5143, 0.51442]的第7個1/10 |
7 |
[0.51439,0.5143948] |
B |
0.51439在間隔[0.51439,0.5143948]的第1個1/10 |
8 |
譯碼的消息:C A D A C D B |
4、遊程編碼
行程編碼的基本原理是:用一個符號值或串長代替具有相同值的連續符號(連續符號構成了一段連續的“行程”。行程編碼因此而得名),使符號長度少於原始數據的長度。只在各行或者各列數據的代碼發生變化時,一次記錄該代碼及相同代碼重複的個數,從而實現數據的壓縮。
常見的遊程編碼格式包括TGA,Packbits,PCX以及ILBM。
例如:5555557777733322221111111
行程編碼爲:(5,6)(7,5)(3,3)(2,4)(1,7)。可見,行程編碼的位數遠遠少於原始字符串的位數。
並不是所有的行程編碼都遠遠少於原始字符串的位數,但行程編碼也成爲了一種壓縮工具。
5、LZ77
算法核心是查找從前向緩衝存儲器開始的最長匹配串
編碼算法的具體執行步驟如下:
把編碼位置設置到輸入數據流的開始位置
查找窗口中最長匹配串
以(Pointer, Length,Characters)的格式輸出,其中Pointer是指向窗口中匹配串的指針,Length表示匹配字符的長度,Characters是前向緩衝存儲器中的不匹配的第1個字符,如果沒有匹配,輸出形式爲( 0, 0, new symbol )
如果前向緩衝存儲器不是空的,則把編碼位置和窗口向前移(Length+1)個字符,然後返回到步驟2
6、LZ78
LZ78算法的基本思路與LZ77算法類似,也是利用已經處理過的編碼信息,但它發生匹配時,不是保存一個三元組,而是一個二元組:匹配位置和不匹配的第一個字符。同時,還要將這個字符串保存到內存中,爲此,它需要一個不斷增長的編碼字串表(字典)。
與LZ77相比,LZ78的最大優點是在每個編碼步驟中減少了字符串比較的數目,而壓縮率與LZ77類似。
如對符號串“ababcbabaaaaaaa”編碼,需要的字典:
編碼步驟
1 在開始時,詞典和當前前綴P都是空的
2 當前字符C:=字符流中的下一個字符
3 判斷P+C是否在詞典中:
a.是:用C擴展P,P:= P+C
b.否:① 輸出與P相對應的碼字和當前字符C
② 把P+C 添加到詞典中
③ 令P:=空值
4.判斷字符流中是否還有字符需要編碼
a.是:返回到步驟2
b.否:若P非空,輸出相應碼字,結束
LZ78編碼示例
ABBCBCABABCAABCAAB
(0,A)(0,B)(2,C)(3,A)(2,A)(4,A)(6,B)
7、LZW
LZW算法-編碼原理
Welch modification (Welch 1984)
爲了解決在發送新字符時的低效問題
與LZ78的區別
LZW只輸出代表詞典中的綴-符串(String)的碼字(code word),因此開始時詞典被初始化爲包含可能在字符流出現中的所有單字符
由於所有可能出現的單個字符都事先包含在詞典中,每個編碼步驟開始時都使用單字符前綴(one-character prefix),因此在詞典中搜索的第1個綴-符串有兩個字符
LZW算法-編碼步驟
1:開始時的詞典包含所有單字符,當前前綴P爲空
2:當前字符C:=字符流中的下一個字符;
3:判斷綴-符串P+C是否在詞典中
(1) 是:P:= P+C; //用C擴展P
(2) 否:
① 把代表當前前綴P的碼字輸出到碼字流;
② 把綴-符串P+C添加到詞典;
③ 令P:= C; //現在的P僅包含一個字符C
4: 判斷碼字流中是否還有碼字要譯
(1) 是,就返回到步驟2;
(2) 否
① 把代表當前前綴P的碼字輸出到碼字流;
② 結束。
LZW算法-編碼示例
LZW算法-解碼步驟
1:在開始譯碼時詞典包含所有單字符
2:cW:=碼字流中的第一個碼字,輸出String.cW到碼字流。
3:先前碼字pW:=當前碼字cW,
4:當前碼字cW:=碼字流中的下一個碼字。
5:判斷String.cW是否在詞典中
(1) 是,則:
① String.cW輸出到字符流
② P:=String.pW,C:=Sting.cW的第一個字符。
③ 把P+C添加到詞典。
(2) 否,則:
① P:=String.pW,C:=String.pW的第一個字符。
② 輸出P+C到字符流,然後把它添加到詞典中。
6: 判斷碼字流中是否還有碼字要譯
(1) 是,就返回到步驟3。
(2) 否, 結束。