Huffman 编码问题

实验内容

编程实现 Huffman 编码问题,并理解其核心思想。
对字符串进行 01 编码,输出编码后的 01 序列,并比较其相对于定长编码的压缩率。例如对于字符串“AABBBEEEEGZ”,如果使用定长编码,‘A’、‘B’、‘E’、‘G’、‘Z’字符各需要 3 位 01 串编码,编码后的字符长度为 311=33 位,如果使用 Huffman 编码,可编码为下图,编码后的字符长度为 23+32+41+4+4=24,压缩率为 24/33=72.73%.

算法设计思路

首先,定义了一个Node类,表示哈夫曼树中的节点。每个节点包含频率(freq)、字符(symbol)以及左子节点(left)和右子节点(right)。
接下来,定义了build_huffman_tree函数,用于构建哈夫曼树。该函数接受一个频率映射freq_map作为输入,其中键为字符,值为字符出现的频率。函数首先将频率映射中的每个字符和频率构建成一个节点,并使用优先队列(堆)进行排序。然后,不断从优先队列中取出频率最小的两个节点,合并为一个新的父节点,并将其插入优先队列中,直到优先队列中只剩下一个节点,即根节点,表示构建完成的哈夫曼树。
然后,定义了generate_huffman_codes函数,用于递归生成哈夫曼编码。该函数接受一个节点root、当前编码code和保存编码的字典huffman_codes作为输入。如果当前节点是叶子节点,即具有字符值,将该字符和对应的编码添加到字典huffman_codes中。否则,递归地处理左子节点和右子节点,分别在当前编码基础上加上"0"和"1",继续生成编码。
接下来,定义了compress_string函数,用于压缩输入的字符串。该函数首先根据输入字符串构建频率映射freq_map,然后调用build_huffman_tree函数构建哈夫曼树,并调用generate_huffman_codes函数生成哈夫曼编码。接着,遍历输入字符串,根据哈夫曼编码将每个字符替换为对应的编码,生成压缩后的字符串。
然后,定义了calculate_initial_bit_length函数,用于计算初始位数。该函数接受哈夫曼编码字典huffman_codes作为输入,计算编码字典的长度,然后不断增加初始位数n,直到能够表示所有编码的位数。
接下来,定义了calculate_compression_ratio函数,用于计算压缩率。该函数接受原始字符串、压缩后的字符串和初始位数n作为输入,计算原始字符串和压缩后的字符串的比特数,然后计算压缩率。
最后,代码从文件orignal.txt中读取输入字符串,并调用上述函数进行压缩和计算。然后,将哈夫曼编码表和压缩率输出到文件table.txt中,并打印每个字符的频率、哈夫曼编码以及压缩率。

源码及注释

import heapq
from collections import defaultdict

# 定义节点类
class Node:
    def __init__(self, freq, symbol, left=None, right=None):
        self.freq = freq
        self.symbol = symbol
        self.left = left
        self.right = right

    # 用于堆排序
    def __lt__(self, other):
        return self.freq < other.freq


# 建立哈夫曼树
def build_huffman_tree(freq_map):
    pq = []
    for symbol, freq in freq_map.items():
        heapq.heappush(pq, Node(freq, symbol))

    while len(pq) > 1:
        left = heapq.heappop(pq)
        right = heapq.heappop(pq)
        parent = Node(left.freq + right.freq, None, left, right)
        heapq.heappush(pq, parent)

    return pq[0]


# 递归生成哈夫曼编码
def generate_huffman_codes(root, code, huffman_codes):
    if root.symbol:
        huffman_codes[root.symbol] = code
        return

    generate_huffman_codes(root.left, code + "0", huffman_codes)
    generate_huffman_codes(root.right, code + "1", huffman_codes)


# 压缩字符串
def compress_string(input_string):
    freq_map = defaultdict(int)
    for char in input_string:
        freq_map[char] += 1

    huffman_tree = build_huffman_tree(freq_map)
    huffman_codes = {}
    generate_huffman_codes(huffman_tree, "", huffman_codes)

    compressed_string = ""
    for char in input_string:
        compressed_string += huffman_codes[char]

    return compressed_string, huffman_codes, freq_map


#计算初始位数
def calculate_initial_bit_length(huffman_codes):
    l = len(huffman_codes)
    n = 0
    while l > pow(2, n):
        n += 1
    return n
    

# 计算压缩率
def calculate_compression_ratio(original_string, compressed_string, n):
    original_bits = len(original_string) * n
    compressed_bits = len(compressed_string)
    return compressed_bits / original_bits



with open('orignal.txt', 'r') as f:
    input_string = f.read()
    f.close()
input_string = list(filter(lambda x: x != " " and x != "\n", input_string))
compressed_string, huffman_codes, freq_map = compress_string(input_string)
n = calculate_initial_bit_length(huffman_codes)
compression_ratio = calculate_compression_ratio(input_string, compressed_string, n)


with open("table.txt", "w",encoding='utf-8') as file:
    file.write('字符   出现频率   编码\n')
    for char, freq in freq_map.items():
        file.write('{} {} {}\n'.format(char, freq, huffman_codes[char]))
    


for char, freq in freq_map.items():
    print(char, ":", freq, "-", huffman_codes[char])

print("压缩率:", compression_ratio)
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值