LZW编码
Posted peri0d
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LZW编码相关的知识,希望对你有一定的参考价值。
原文:https://segmentfault.com/a/1190000011425787?utm_source=tag-newest
LZW 算法引入
核心思想:把出现过的字符串映射到记号上,这样就可能用较短的编码来表示长的字符串,实现压缩
比如对于字符串
ABABAB
可以看到字串AB
在后面重复了,就可以用一个特殊的记号表示AB
,比如使用 2,原来的字符串就表示为AB22
,那么 2 就可以称为AB
的记号那么如果规定 0 表示 A,1 表示 B,实际上最后得到的压缩后的数据为一个记号流
0122
,这样就有了一个记号和字符串的映射表,即字典如下:
压缩后的编码
0122
,根据字典就可以解码为原字符了但是在真正的 LZW 中,A 和 B 是用 ASCII 码表示的
LZW 初始会有一个默认的字典,包含了所有 256 个 8 bit 字符,单个字符的记号就是它自身,用数字表示就是 ASCII 值
在编码过程中加入的新的记号的映射,从256开始,称为扩展表
字典生成
- 为什么第一个
AB
不也用 2 表示? - LZW 有一个核心思想:压缩后的编码是自解释的
- 字典是不会被写进压缩文件的,在解压缩的时候,一开始字典里除了默认的 0->A 和 1->B 之外并没有其它映射
- 2->AB 是在解压缩的过程中一边加入的。这就要求压缩后的数据自己能告诉解码器,完整的字典,例如2->AB,是如何生成的,在解码的过程中还原出编码时用的字典
ABABAB
编码过程:- 遇到 A,用 0 表示,编码 0
- 遇到 B,用 1 表示,编码 1
- 发现子串 AB,添加映射 2->AB 到字典
- 遇到 AB,用 2 编码
- 可以看出最前面的 A 和 B 是用来生成表项 2->AB 的,所以它们必须被保留在压缩编码里,作为表项 2->AB 第一次生成的地方
- 在解码
0122
的时候,解码器首先通过 01 直接解析出最前面 A 和 B,并且生成表项 2->AB,这样才能将后面出现的 2 都解析为 AB - 编码和解码都是从前往后步步推进的,同时生成字典,所以解码的过程也是一个不断还原编码字典的过程。解码器一边解码,向后推进,一边在之前已经解出的原始数据上重现编码的过程,构建出编码时用的字典。
LZW 算法详解
编码算法
- 编码器从原字符串不断地读入新的字符,并试图将单个字符或字符串编码为记号
- 这里我们维护两个变量,一个是 P (Previous),表示手头已有的,还没有被编码的字符串,一个是 C (current),表示当前新读进来的字符
算法
- 初始状态,字典里只有所有的默认项,例如 0->a,1->b,2->c。此时P 和 C都是空的
- 读入新的字符 C,与 P 合并形成字符串 P+C
- 在字典里查找 P+C,
- 如果 P+C 在字典里,P=P+C
- 如果 P+C 不在字典里,将 P的记号输出;在字典中为 P+C建立一个记号映射;更新 P=C
- 返回步骤 2 重复,直到读完所有字符
P 是当前维护的,可以被编码为记号的子串。注意 P 是可以被编码为记号,但还并未输出。
新的字符 C 不断被读入并添加到 P 的尾部,只要 P+C 仍然能在字典里找到,就不断增长更新 P=P+C,这样就能将一个尽可能长的字串 P 编码为一个记号,这就是压缩的实现
编码例子
压缩
ababcababac
初始状态字典:
开始编码
结果为
0132372
注意编码过程中的第 3-4 步,第 7-8步 以及8-10步,子串 P 发生了增长,直到新的 P+C 无法在字典中找到,则将当前的 P 输出,P 则更新为单字符 C,重新开始增长
LZW 编码 Python 实现
下面的代码是用 0->A,1->B,2->C,作为基本编码表的
s = input("Please input a string: ")
def init(s):
dict = {}
tmp = []
for i in s:
if i not in tmp:
tmp.append(i)
# print(tmp)
j = 0
for i in range(len(tmp)):
t = str(j)
dict[tmp[i]] = t
j = j + 1
# print(dict)
return dict
def encoding(s):
r = []
table = init(s)
P = ''
for i in range(len(s)):
C = s[i]
t = P+C
if t in table.keys():
P = t
continue
if t not in table.keys():
# t=AB
table[t] = str(len(table))
r.append(table[P])
P = C
if i == len(s)-1:
r.append(table[s[i]])
print("After compress: " + ''.join(r))
print("coding table is: ")
print(table)
encoding(s)
以上是关于LZW编码的主要内容,如果未能解决你的问题,请参考以下文章