概述
香农算法(Shannon-Fano 编码)是克劳德·香农在1948年发表的《A Mathematical Theory of Communication》中提出的一种基于数据统计的无损压缩编码算法。该算法主要通过计算不同字符在原始数据中出现的频率,并基于这些频率为每个字符分配一个唯一的编码来实现无损压缩。
编码过程
香农算法的编码过程如下:
统计字符出现频率:
遍历原始数据记录所有不同字符出现的频率。
构建哈夫曼树:
将所有字符及其频率构成叶子节点,按频率大小把两个叶子节点合并为一个新节点,直到哈夫曼树的根节点为止。
遍历哈夫曼树,为每个叶子节点编号:
从根节点开始,深度优先遍历哈夫曼树,为每个叶子节点分配一个唯一的二进制编码,将左子树标记为0,右子树标记为1。
编码特点
香农算法的编码特点如下:
- 每个字符的编码是唯一的,并具有前缀码的特性——任何一个字符的编码都不是另一个字符编码的前缀,因此可以保证解码时不会产生歧义。
- 编码长度与字符出现频率有关,出现频率越高的字符,其对应的二进制编码越短。
- 香农算法可以实现无损压缩,即解压缩后可以完全恢复原始数据,但对于某些特定的数据集,其压缩率并不一定最优。
应用场景
香农算法在以下场景中得到了广泛应用:
- 文本压缩:对于较长的文本文件,通过香农算法进行压缩,可以减少文件的存储和传输成本。
- 图像压缩:基于JPEG2000标准的图像压缩算法就采用了香农算法。
- 信息论研究:香农算法是信息熵和信息熵编码的基础,可以为学者们提供更多探索信息理论领域的思路。
缺点
香农算法虽然可以实现无损压缩,但存在以下缺点:
-
无法动态适应:由于编码表是事先生成的,因此当输入的数据集发生变化时,需要重新生成编码表。
-
压缩率不一定最优:尽管香农算法可以实现无损压缩,但对于某些特定的数据集,其压缩率并不一定最优。因此,在实际应用中,需要针对数据集和应用场景进行选择。
综述
香农算法是一种经典的无损压缩算法,已经被广泛应用于文本压缩、图像压缩以及信息论研究等领域。虽然香农算法有着自身的缺点,但它的编码过程简洁易懂,具有较高的逻辑性和实用性,能够为小白用户提供压缩数据的一种思路。
香农算法python源码
import numpy as np
def buildCodeTable(codeTable, cumProb, index, code):
if index > len(codeTable):
return codeTable
if cumProb[index-1] <= 0.5:
codeTable[index-1][1] = code + '0'+' '
codeTable = buildCodeTable(codeTable, cumProb, index+1, code+'0')
else:
codeTable[index-1][1] = code + '1'+' '
codeTable = buildCodeTable(codeTable, cumProb, index+1, code+'1')
return codeTable
def shannonCoding(text):
# 计算字符频率
symbols = list(set(text))
freq = [(text.count(s) + 1) / (len(text) + len(set(text))) for s in symbols]
# 计算累积概率
cumProb = np.cumsum(freq)
# 输出中间结果,调试用
print('symbols:', symbols)
print('freq:', freq)
print('cumProb:', cumProb)
# 构建编码表
codeTable = [[symbols[i], ''] for i in range(len(symbols))]
codeTable = buildCodeTable(codeTable, cumProb, 1, '')
# 输出编码表,调试用
print('codeTable:', codeTable)
# 编码
encoded = ''.join([codeTable[symbols.index(s)][1] for s in text])
# 解码
decoded = ''
code = ''
for b in encoded:
code += b
i = -1
for j, c in enumerate(codeTable):
if c[1] == code:
i = j
break
if i >= 0:
decoded += codeTable[i][0]
code = ''
# 计算平均码长
codeLengths = [len(c[1]) for c in codeTable]
avgCodeLength = sum([c * f for c, f in zip(codeLengths, freq)])
# 计算编码效率
efficiency = 1 / avgCodeLength
return encoded, decoded, avgCodeLength, efficiency,codeTable
text = 'hello world'
encoded, decoded, avgCodeLength, efficiency,codeTable = shannonCoding(text)
print('编码结果:')
print(encoded)
print('解码结果:')
print(decoded)
print('平均码长:')
print(avgCodeLength)
print('编码效率:')
print(efficiency)
print('编码表:')
print(codeTable)