LZ78編碼
LZ78算法,建立詞典的算法。
LZ78的編碼思想:
不斷地從字符流中提取新的綴-符串(String),通俗地理解為新"詞條",然后用"代號"也就是碼字(Code word)表示這個"詞條"。
對字符流的編碼就變成了用碼字(Code word)去替換字符流(Charstream),生成碼字流(Codestream),從而達到壓縮數據的目的。
幾個約定:
- 字符流(Charstream):要被編碼的數據序列。
- 字符(Character):字符流中的基本數據單元。
- 前綴(Prefix): 在一個字符之前的字符序列。
- 綴-符串(String):前綴+字符。
- 碼字(Code word):編碼以后在碼字流中的基本數據單元,代表詞典中的一串字符
- 碼字流(Codestream): 碼字和字符組成的序列,是編碼器的輸出
- 詞典(Dictionary): 綴-符串表。按照詞典中的索引號對每條綴-符串(String)指定一個碼字(Code word)
- 當前前綴(Current prefix):在編碼算法中使用,指當前正在處理的前綴,用符號P表示
- 當前字符(Current character):在編碼算法中使用,指當前前綴之后的字符,用符號Char表示。
- 當前碼字(Current code word): 在譯碼算法中使用,指當前處理的碼字,用W表示當前碼字,String.W表示當前碼字的綴-符串。
編碼算法步驟:
步驟1: 在開始時,詞典和當前前綴P 都是空的。
步驟2: 當前字符Char :=字符流中的下一個字符。
步驟3: 判斷P+Char是否在詞典中:
(1) 如果"是":用Char擴展P,讓P := P+Char ;
(2) 如果"否":① 輸出與當前前綴P相對應的碼字和當前字符Char;
② 把字符串P+Char 添加到詞典中。③ 令P :=空值。
(3) 判斷字符流中是否還有字符需要編碼
① 如果"是":返回到步驟2。
② 如果"否":若當前前綴P不空,輸出相應於當前前綴P的碼字,結束編碼。
解碼算法步驟:
步驟1:在開始時詞典為空;
步驟2:當前碼字W:= 碼字流中的下一個碼字
步驟3:當前字符Char:=緊隨碼字之后的字符
步驟4:把當前碼字的綴-符串(string.W)輸出到字符流,然后輸出字符Char
步驟5:把string.W + Char添加到詞典中
步驟6:判斷碼字流中是否還有碼字要譯碼,
(1)如果有,返回步驟2 (2)如果沒有,則結束
代碼實現(C#):
-
/// <summary>
-
/// LZ78編碼所需詞典
-
/// </summary>
-
public struct Dictionary
-
{
-
public int id;
-
public string context;
-
public Dictionary(int id, string str)
-
{
-
this.id = id;
-
this.context = str;
-
}
-
}
-
/// <summary>
-
/// 編碼器類
-
/// </summary>
-
public static class Encoder
-
{
-
/// <summary>
-
/// 詞典
-
/// </summary>
-
static List<Dictionary> D = new List<Dictionary>();
-
-
/// <summary>
-
/// 在詞典中查找相應串
-
/// </summary>
-
/// <param name="item"></param>
-
/// <param name="D"></param>
-
/// <returns></returns>
-
static bool Find(string item, List<Dictionary> D)
-
{
-
foreach (Dictionary d in D)
-
if (d.context == item)
-
return true;
-
return false;
-
}
-
-
/// <summary>
-
/// 根據詞典條目內容查找相應編號
-
/// </summary>
-
/// <param name="item"></param>
-
/// <param name="D"></param>
-
/// <returns></returns>
-
static int GetDicID(string item, List<Dictionary> D)
-
{
-
foreach (Dictionary d in D)
-
if (d.context == item)
-
return d.id;
-
return 0;
-
}
-
-
/// <summary>
-
/// 將一個條目加入詞典
-
/// </summary>
-
/// <param name="item"></param>
-
/// <param name="D"></param>
-
static void AddToDic(string item, List<Dictionary> D)
-
{
-
int maxID;
-
if (D.Count == 0)
-
maxID = 0;
-
else
-
maxID = D.Last().id;
-
-
D.Add(new Dictionary(maxID + 1, item));
-
}
-
-
/// <summary>
-
/// 顯示詞典
-
/// </summary>
-
public static void ShowDictionary()
-
{
-
Console.WriteLine("Dictionary:");
-
foreach (Dictionary d in D)
-
{
-
Console.WriteLine("<{0},{1}>", d.id, d.context);
-
}
-
}
-
-
/// <summary>
-
/// 執行LZ78編碼算法
-
/// </summary>
-
/// <param name="str"></param>
-
public static void Execute(string str)
-
{
-
StringBuilder P = new StringBuilder();
-
char CHAR;
-
P.Clear();
-
foreach (char c in str)
-
{
-
CHAR = c;
-
if (Find((P.ToString() + CHAR.ToString()), D))
-
P.Append(CHAR);
-
else
-
{
-
Console.Write("({0},{1})", GetDicID(P.ToString(), D), c);
-
AddToDic(P.ToString() + c.ToString(), D);
-
P.Clear();
-
}
-
}
-
if (P.ToString() != "")
-
Console.Write("({0},{1})", GetDicID(P.ToString(), D), "/");
-
Console.WriteLine();
-
}
-
}
-
/// <summary>
-
/// 解碼器類
-
/// </summary>
-
public static class Decoder
-
{
-
/// <summary>
-
/// 內部類:將碼字字符串轉換為編碼數組
-
/// </summary>
-
struct Codes
-
{
-
public int id;
-
public char code;
-
public Codes(int id, char code)
-
{
-
this.id = id;
-
this.code = code;
-
}
-
}
-
-
/// <summary>
-
/// 詞典
-
/// </summary>
-
static List<Dictionary> D = new List<Dictionary>();
-
-
/// <summary>
-
/// 碼字流,從字符串中獲取
-
/// </summary>
-
static List<Codes> CodeStream = new List<Codes>();
-
-
/// <summary>
-
/// 將碼字串變為碼字流
-
/// </summary>
-
/// <param name="str"></param>
-
static void BuildCodes(string str)
-
{
-
/******************
-
* stauts 定義:
-
* 0: 開始/結束狀態
-
* 1: 逗號之前
-
* 2: 逗號之后
-
******************/
-
int status = 0;
-
int id = 0;
-
char code = (char)0;
-
string number = "";
-
foreach (char c in str)
-
{
-
if (c == '(')
-
status = 1;
-
-
else if (status == 1 && c != ',')
-
number += c;
-
-
else if (c == ',')
-
{
-
status = 2;
-
id = Convert.ToInt32(number);
-
number = "";
-
}
-
-
else if (status == 2)
-
{
-
code = c;
-
status = 0;
-
}
-
-
else if (c == ')')
-
CodeStream.Add(new Codes(id, code));
-
}
-
}
-
-
/// <summary>
-
/// 將一個條目加入詞典
-
/// </summary>
-
/// <param name="item"></param>
-
/// <param name="D"></param>
-
static void AddToDic(string item, List<Dictionary> D)
-
{
-
int maxID;
-
if (D.Count == 0)
-
maxID = 0;
-
else
-
maxID = D.Last().id;
-
-
D.Add(new Dictionary(maxID + 1, item));
-
}
-
-
/// <summary>
-
/// 根據詞典序號找出詞典內容
-
/// </summary>
-
/// <param name="id"></param>
-
/// <param name="D"></param>
-
/// <returns></returns>
-
static string GetContext(int id, List<Dictionary> D)
-
{
-
foreach (Dictionary d in D)
-
{
-
if (d.id == id)
-
return d.context;
-
}
-
return string.Empty;
-
}
-
-
/// <summary>
-
/// 執行LZ78譯碼算法
-
/// </summary>
-
/// <param name="str"></param>
-
public static void Execute(string str)
-
{
-
int W;
-
char CHAR;
-
string original;
-
-
BuildCodes(str);
-
foreach (Codes c in CodeStream)
-
{
-
W = c.id;
-
if (c.code != '/')
-
CHAR = c.code;
-
else CHAR = (char)0;
-
if (W == 0)
-
{
-
Console.Write(CHAR);
-
AddToDic(CHAR.ToString(), D);
-
}
-
else
-
{
-
original = GetContext(W, D);
-
Console.Write(original + CHAR.ToString());
-
AddToDic(original + CHAR.ToString(), D);
-
}
-
}
-
Console.WriteLine();
-
}
-
}
執行效果(主界面程序代碼省略):
可見算法執行的結果是完全正確的。