文章目录
https://blog.csdn.net/google19890102/article/details/54848262
https://zh.wikipedia.org/wiki/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81
0. 从霍夫曼编码讲起
霍夫曼树是为了获得霍夫曼编码而提出的。霍夫曼编码是一种无损的数据压缩编码,采用变长二进制编码的方式来对符号数据进行编码。它是通过评估符号出现的概率来设计不同符号的编码长度,出现概率高的符号采用较短的编码,出现概率较低的符号采用较长的编码。可以证明,霍夫曼编码是一种最优的二进制编码方式(前提是知道符号和符号出现的概率)。生成霍夫曼编码的数据结构被称为霍夫曼树,它是一种带权路径长度最小的二叉树,也被称为最优二叉树。
1. 二叉树一些基本概念
1.1. 路径
从一个节点到另一个节点的通路
1.2. 路径长度
路径上的边的条数
1.3. 节点的权
节点上的权重值
1.4. 节点的带权路径长度
根节点到某个节点的路径长度乘以该节点的权重值
1.5. 树的带权路径长度
所有叶子节点的带权路径长度之和
2. Huffman树的构建
- 统计所有字符的频率,按照从小到大的顺序排序,记排序好的集合为S。
- 若S中的元素个数小于2,则结束
- 取出S中频率最小的两个元素,记较小的元素为e1,较大的元素为e2
- 以e1为左孩子,e2为右孩子,它们的权重即为各自的频率。并创建一个父节点e3,e3的权重为e1和e2之和。
- 将e3放入到S中,重新排序
- 跳转到2
3. 代码实现(python)
有参考https://zhuanlan.zhihu.com/p/103908133, 在其基础上进行了优化。输入和输出都假定是字符串。
节点类:
class Node:
def __init__(self, frequency):
self.left = None
self.right = None
self.father = None
self.frequency = frequency
def is_left(self):
return self.father.left == self
工具函数:
# 统计字符出现频率,生成映射表
def count_frequency(text):
chars = []
ret = []
for char in text:
if char in chars:
continue
else:
chars.append(char)
ret.append((char, text.count(char)))
return ret
创建叶子节点:
# 创建叶子节点
def create_leaves(frequency_list):
return [Node(frequency) for frequency in frequency_list]
创建Huffman树:
# 创建Huffman树
def create_huffman_tree(leaves):
queue = leaves[:]
queue.sort(key=lambda item: item.frequency,reverse=True)
while len(queue) > 1:
node_left = queue.pop(-1)
node_right = queue.pop(-1)
node_father = Node(node_left.frequency + node_right.frequency)
node_father.left = node_left
node_father.right = node_right
node_left.father = node_father
node_right.father = node_father
len_queue=len(queue)
if len_queue==0:
queue.append(node_father)
break
if node_father.frequency>queue[0].frequency:
queue.insert(0,node_father)
continue
for i in range(0,len_queue):
if node_father.frequency<=queue[len_queue-1-i].frequency:
queue.insert(len_queue-1-i+1,node_father)
break
queue[0].father = None
return queue[0]
Huffman编码,得到{char:code}字典:
# Huffman编码,得到{char:code}字典
def huffman_encoding(leaves, root,char_frequency):
char_to_code={}
for char,frequency in char_frequency:
char_to_code[char]=""
for i in range(len(leaves)):
leaf = leaves[i]
while leaf != root:
if leaf.is_left():
char_to_code[char_frequency[i][0]] = '0' + char_to_code[char_frequency[i][0]]
else:
char_to_code[char_frequency[i][0]] = '1' + char_to_code[char_frequency[i][0]]
leaf = leaf.father
return char_to_code
编码整个字符串:
# 编码整个字符串
def encode_str(text, char_to_code):
ret = ''
for char in text:
ret +=char_to_code[char]
return ret
解码整个字符串:
# 解码整个字符串
def decode_str(huffman_str, char_to_code):
ret = ''
while huffman_str != '':
i = 0
for char,code in char_to_code.items():
if code in huffman_str and huffman_str.index(code) == 0:
ret += char
huffman_str = huffman_str[len(code):]
i += 1
return ret
完整代码及测试用例见https://github.com/lankuohsing/DataStructureInPython/blob/main/tree/binary_tree/huffman_tree/huffman_code1.py ,欢迎给个star和follow!
4. 应用
对量化后的AI模型权重进行霍夫曼压缩编码,可以减小存储空间和网络下载时延。此时每个权重值是整型(一般为int8),可以看做一个字符