聊聊字典編碼

JavaEdge發表於2018-12-28

最近由於課程設計需要做解壓縮演算法

特此來考察字典編碼

1 導論

許多場合,開始時不知道要編碼資料的統計特性,也不一定允許你事先知道它們的統計特性。因此,人們提出了許許多多的資料壓縮方法,企圖用來對這些資料進行壓縮編碼,在實際編碼過程中以儘可能獲得最大的壓縮比。這些技術統稱為通用編碼技術。
字典編碼(dictionary encoding)技術(以下簡稱DE)就是屬於這一類,這種技術屬於無失真壓縮技術。

  • DE根據的是資料本身包含有重複程式碼這個特性
    例如文字檔案和光柵影像就具有這種特性

1.1 分類

種類很多,歸納起來大致有兩類

1.1.1 查詢正在壓縮的字元序列是否在歷史輸入資料中出現過

用已經出現過的字串替代重複部分,輸出僅僅是指向之前出現過的字串的“指標”

  • 這裡的“字典”指 用以前處理過的資料來表示編碼過程中遇到的重複部分
    這類編碼的所有演算法都是以abraham lempeljakob ziv在1977年研究發表的稱為lz77演算法為基礎

1.1.2 從輸入的資料中建立一個“短語字典(dictionary of the phrases)”

這種短語不一定是像“好好學習天天向上”和“你個糟老頭子壞得很我信你個鬼”這類具有具體含義的短語,它可以是任意字元的組合
編碼資料過程中當遇到已經在字典中出現的“短語”時,編碼器就輸出這個字典中的短語的“索引號”,而不是短語本身。
  
j.ziva.lempel在1978年首次發表了這種編碼方法的文章
在他們的研究基礎上,terry a.weltch在1984年發表了改進這種編碼演算法的文章,因此把這種編碼方法稱為LZW(lempel-ziv walch)壓縮編碼,首先在高速硬碟控制器上應用了這種演算法

2 LZ77演算法

2.1 常見術語

  • 輸入資料流(input stream)
    要被壓縮的字元序列
  • 字元(character)
    輸入資料流中的基本單元。
  • 編碼位置(coding position)
    輸入資料流中當前要編碼的字元位置,指前向緩衝儲存器中的開始字元
  • 前向緩衝儲存器(Lookahead buffer)
    存放從編碼位置到輸入資料流結束的字元序列的儲存器。
  • 視窗(window)
    包含W個字元的視窗,字元是從編碼位置開始向後數也就是最後處理的字元數
  • 指標(pointer)
    指向視窗中的匹配串且含長度的指標

核心是查詢從前向緩衝儲存器開始的最長的匹配串

2.2 執行步驟

1.把編碼位置設定到輸入資料流的起始位
2.查詢視窗中最長的匹配串
3.以“(Pointer, Length) Characters”的格式輸出
其中Pointer是指向視窗中匹配串的指標,Length表示匹配字元的長度,Characters是前向緩衝儲存器中的不匹配的第1個字元。
4.如果前向緩衝儲存器不是空的,則把編碼位置和視窗向前移(Length+1)個字元,然後返回到步驟2

[例]

待編碼的資料流
編碼過程

  • “步驟” 編碼步驟
  • “位置” 編碼位置,輸入資料流中的第1個字元為編碼位置1
  • “匹配串” 視窗中找到的最長的匹配串
  • “字元” 匹配後在前向緩衝儲存器中的第1個字元
  • “輸出” 以“(Back_chars, Chars_length) Explicit_character”格式輸出

    • (Back_chars, Chars_length) 指向匹配串的指標
      告訴譯碼器在這個視窗中向後退Back_chars個字元然後拷貝Chars_length個字元到輸出
    • Explicit_character 真實字元
      例如,表編碼過程的輸出(5,2) C告訴譯碼器回退5個字元,然後拷貝2個字元“AB”

但wikipedia認為,粗體字理解成
從編碼位置開始往回數Back_chars個字元,從該字元開始數起的字串與接下來的Chars_length個字元完全相同
原文

3 LZ78演算法

3.1 術語和符號

  • 字元流(Charstream)
    要被編碼的資料序列
  • 字元(Character)
    字元流中的基本資料單元
  • 字首(Prefix)
    在一個字元之前的字元序列

-綴-符串(String)
字首+字元

  • 碼字(Code word)
    碼字流中的基本資料單元,代表字典中的一串字元
  • 碼字流(Codestream)
    碼字和字元組成的序列,編碼器的輸出
  • 字典(Dictionary)
    綴-符串表。按照字典中的索引號對每條綴-符串(String)指定一個碼字(Code word)
  • 當前字首(Current prefix)
    在編碼演算法中使用,指當前正在處理的字首,用符號P表示。
  • 當前字元(Current character)
    在編碼演算法中使用,指當前字首之後的字元,用符號C表示。
  • 當前碼字(Current code word)
    在譯碼演算法中使用,指當前處理的碼字,用W表示當前碼字,String.W表示當前碼字的綴-符串

3.2 編碼演算法

不斷從字元流中提取新的綴-符串(String),通俗地理解為新“詞條”,然後用“代號”也就是碼字(Code word)表示這個“詞條”
這樣一來,對字元流的編碼就變成了用碼字(Code word)去替換字元流(Charstream),生成碼字流(Codestream),從而達到壓縮資料的目的

在編碼開始時字典是空的,不包含任何綴-符串(string)。在這種情況下編碼器就輸出一個表示空字串的特殊碼字(例如“0”)和字元流中(Charstream)的第一個字元C,並把這個字元C新增到字典中作為一個由一個字元組成的綴-符串(string)。在編碼過程中,如果出現類似的情況,也照此辦理。在字典中已經包含某些綴-符串(String)之後,如果“當前字首P +當前字元C”已經在字典中,就用字元C來擴充套件這個字首,這樣的擴充套件操作一直重複到獲得一個在字典中沒有的綴-符串(String)為止。此時就輸出表示當前字首P的碼字(Code word)和字元C,並把P+C新增到字典中作為字首(Prefix),然後開始處理字元流(Charstream)中的下一個字首。

LZ78編碼器的輸出是碼字-字元(W,C)對,每次輸出一對到碼字流中,與碼字W相對應的綴-符串(String)用字元C進行擴充套件生成新的綴-符串(String),然後新增到字典中

具體演算法

步驟1

在開始時,字典和當前字首P都是空的

步驟2

當前字元C:=字元流中的下一個字元

步驟3

P+C 是否在字典中
(1) “是”
用C擴充套件P,讓P := P+C
(2) “否”
    ① 輸出與當前字首P相對應的碼字和當前字元C
    ② 把字串P+C 新增到字典中
    ③ 令P:=空值
(3) 字元流中是否還有字元需要編碼
    ① “是” 返回到步驟2
    ② “否” 若當前字首P非空,輸出相應於當前字首P的碼字,然後結束編碼

3.3 譯碼演算法

在譯碼開始時譯碼字典為空,它將在譯碼過程中從碼字流中形成
每當從碼字流中讀入一對碼字-字元(W,C)對時,碼字就參考已經在字典中的綴-符串,然後把當前碼字的綴-符串string
W 和字元C輸出到字元流(Charstream),而把當字首-符串(string.W+C)新增到字典中。在譯碼結束之後,重構的字典與編碼時生成的字典完全相同

具體演算法

步驟1

開始時字典空

步驟2

當前碼字W :=碼字流中的下一個碼字

步驟3

當前字元C := 緊隨碼字之後的字元。

步驟4

把當前碼字的綴-符串(string.W)輸出到字元流(Charstream),然後輸出字元C

步驟5

把string.W+C新增到字典中

步驟6

判斷碼字流中是否還有碼字要譯
   (1) “是” 返回到步驟2
   (2) “否” 結束

編碼字串
編碼過程
●“步驟” 編碼步驟
●“位置” 在輸入資料中的當前位置
●“字典” 新增到字典中的綴-符串,綴-符串的索引等於“步驟”序號
●“輸出” 以(當前碼字W, 當前字元C)簡化為(W, C)的形式輸出

與LZ77相比,LZ78的最大優點是在每個編碼步驟中減少了綴-符串(String)比較的數目,而壓縮率與LZ77類似

4 LZW演算法

使用的術語與LZ78的類似,僅增加了一個術語—字首根(Root),它是由單個字串組成的綴-符串(String)

4.1 編碼原理

  • LZW只輸出代表字典中的綴-符串(String)的碼字(code word)
    意味著在開始時字典不能是空的,它必須包含可能在字元流出現中的所有單個字元,即字首根(Root)
  • 由於所有可能出現的單個字元都事先包含在字典中,每個編碼步驟開始時都使用一字元字首(one-character prefix),因此在字典中搜尋的第1個綴-符串有兩個字元

4.2 編碼演算法

LZW編碼是圍繞稱為字典的轉換表來完成的
這張轉換表用來存放稱為字首(Prefix)的字元序列,並且為每個表項分配一個碼字(Code word),或者叫做序號
詞典
這張轉換表實際上是把8位ASCII字符集進行擴充,增加的符號用來表示在文字或影像中出現的可變長度ASCII字串
擴充後的程式碼可用9位、10位、11位、12位甚至更多的位來表示
Welch的論文中用了12位,12位可以有4096個不同的12位程式碼,這就是說,轉換表有4096個表項,其中256個表項用來存放已定義的字元,剩下3840個表項用來存放字首(Prefix)

LZW編碼器就是通過管理這個字典完成輸入/輸出的轉換

  • 輸入是字元流(Charstream)
    可以是8位ASCII字元組成的字串
  • 輸出是用n位(例如12位)表示的碼字流(Codestream)
    碼字代表單個字元或多個字元組成的字串。

LZW編碼器使用了一種很實用的分析(parsing)演算法,稱為貪婪分析演算法(greedy parsing algorithm)
在貪婪分析演算法中,每一次分析都要序列地檢查來自字元流(Charstream)的字串,從中分解出已經識別的最長的字串,也就是已經在字典中出現的最長的字首(Prefix)
用已知的字首(Prefix)加上下一個輸入字元C也就是當前字元(Current character)作為該字首的擴充套件字元,形成新的擴充套件字串——綴-符串(String):Prefix.C
這個新的綴-符串(String)是否要加到字典中,還要看字典中是否存有和它相同的綴-符串String

  • 如果有,那麼這個綴-符串(String)就變成字首(Prefix),繼續輸入新的字元
  • 否則就把這個綴-符串(String)寫到字典中生成一個新的字首(Prefix),並給一個程式碼

演算法的執行

步驟1

開始時的字典包含所有可能的根(Root),而當前字首P是空的

步驟2

當前字元(C) :=字元流中的下一個字元

步驟3

判斷綴-符串P+C是否在字典中
(1) “是” :P := P+C
(2) “否”
① 把代表當前字首P的碼字輸出到碼字流;
② 把綴-符串P+C新增到字典;
③ 令P := C

步驟4

碼字流中是否還有碼字要譯
(1) “是” 回到步驟2
(2) “否”
① 把代表當前字首P的碼字輸出到碼字流
② 結束

LZW編碼演算法可用偽碼錶示
開始時假設編碼字典包含若干個已經定義的單個碼字

4.3 譯碼演算法

  • 當前碼字(Current code word)
    當前正在處理的碼字,用cW表示,用string.cW表示當字首-符串
  • 先前碼字(Previous code word)
    先於當前碼字的碼字,用pW表示,用string.pW表示先字首-符串

演算法開始時,譯碼字典與編碼字典相同,包含所有可能的字首根(roots)
在譯碼過程中會記住先前碼字(pW),從碼字流中讀當前碼字(cW)之後輸出當字首-符串string.cW,然後把用string.cW的第一個字元擴充套件的先字首-符串string.pW新增到字典中。

執行步驟

  步驟1:在開始譯碼時字典包含所有可能的字首根(Root)。
  步驟2:cW :=碼字流中的第一個碼字。
  步驟3:輸出當字首-符串string.cW到碼字流。
  步驟4: 先前碼字pW := 當前碼字cW。
  步驟5: 當前碼字cW := 碼字流中的下一個碼字。
  步驟6: 判斷先字首-符串string.pW是否在字典中
   (1) 如果“是”,則:
    ① 把先字首-符串string.pW輸出到字元流。
    ② 當前字首P :=先字首-符串string.pW。
    ③ 當前字元C :=當前字首-符串string.cW的第一個字元。
    ④ 把綴-符串P+C新增到字典。
   (2) 如果“否”,則:
    ① 當前字首P :=先字首-符串string.pW。
    ② 當前字元C :=當字首-符串string.cW的第一個字元。
    ③ 輸出綴-符串P+C到字元流,然後把它新增到字典中。
  步驟7: 判斷碼字流中是否還有碼字要譯
   (1) 如果“是”,就返回到步驟4。
   (2) 如果“否”, 結束。

編碼字串
編碼過程

  • “字典” 新增到字典中的綴-符串,它的索引在括號中
    譯碼過程

每個譯碼步驟譯碼器讀一個碼字,輸出相應的綴-符串,並把它新增到字典中
例如,在步驟4中,先前碼字(2)儲存在先前碼字(pW)中,當前碼字(cW)是(4),當字首-符串string.cW是輸出(“A B”),先字首-符串string.pW (“B”)是用當字首-符串string.cW (“A”)的第一個字元,其結果(“B A”) 新增到字典中,它的索引號是(6)

LZW演算法得到普遍採用,它的速度比使用LZ77演算法的速度快,因為它不需要執行那麼多的綴-符串比較操作
對LZW演算法進一步的改進是增加可變的碼字長度,以及在字典中刪除老的綴-符串
在GIF影像格式和UNIX的壓縮程式中已經採用了這些改進措施之後的LZW演算法


相關文章