zoukankan      html  css  js  c++  java
  • 哈夫曼压缩

    与哈夫曼相关的几个概念

    哈夫曼编码可以称之为一种编码,这种编码又能作为一种压缩算法,所以又叫哈夫曼压缩
    哈夫曼编码的实现方式需要通过建立一棵哈夫曼树,这种树不仅仅可以是二叉树,也可以是任意叉树,n叉树对应于n进制编码每个结点。
    对于n叉树,一开始有m个结点,因为最终整棵哈夫曼树的叶子结点总数为n+(n-1)*k,要尽量让离根近的结点充满,离根远的结点稀疏。所以,一开始需要添加几个频率为0的空结点,添加的结点个数为(m-1)%(n-1).实际上,也可以不添加空结点,第一次从优先队列中弹出时,弹出元素的个数为:n-(m-1)%(n-1)
    哈夫曼编码是一种变长编码,它畸轻畸重,有所侧重,让出现频率高的事物用短码来表示,出现频率低的事物用长代码来表示,这样就能在整体上实现压缩。
    给定一篇文档,首先可以认为这篇文档是由定长编码写成的,称每个定长的东西为单词。假定每个单词的长度都是8bit,对这些单词进行哈夫曼编码之后,有的单词出现次数多,用2bit表示;有的单词出现次数少,用17bit表示。可以断定,至少存在一个单词哈夫曼编码之后比原来长,除非原文中各个单词等频出现。

    关于压缩

    哈夫曼压缩算法需要将哈夫曼编码表打包进文件,而这张表也是需要占据一定的空间的,这可能会导致压缩之后没有变小反而变大。需要设计一种压缩格式,首部一部分描述哈夫曼编码表,body部分描述原来的数据。
    如果不停地压缩一个文件,则生成一个压缩序列x1->x2->x3->x4->x5,这个序列有什么规律,它是有限序列吗,它是否像“泵浦引理”描述的那样包含一个无限循环,它是否会收敛到某个固定值,它是否像二次函数一样先变小后变大,它的函数曲线是否会不停震荡(像高次函数一样).也就是说,这个序列是无限循环小数还是无限不循环小数(无理数)还是有限小数?
    这就需要添加编码器和解码器(解码器用来验证编码器是否正确),用编码器来测试压缩序列的变化。
    下面我来学学费马,“我想到一个绝妙的办法,但是今晚上太困了,写不下去了”。

    实验结果

    本程序只估计一下哈夫曼压缩的效果如何,并没有实现编码器和解码器。本程序把每个单词的长度当成8bit。
    单位一切都是bit

    • 一个mp4文件,压缩之后略微小了一点
      before 184114848
      after 184101221
    • 一个doc文档,压缩之后几乎变为原来的二分之一
      before 290816
      after 154681

    代码

    import queue
    
    
    class Node:
        def __init__(self, word=None, count=0, left=None, right=None):
            self.word = word  # 对应的实际二进制值
            self.count = count  # 在文档中出现的次数
            self.left = left  # 左儿子
            self.right = right  # 右儿子
            self.code = None  # 编码之后的二进制值
    
        def __lt__(self, other):
            return self.count < other.count
    
        def __str__(self):
            return "(%s,%s,%s)" % (self.word, self.count, self.code)
    
    
    def cnt(data):
        a = {}
        for i in data:
            if not i in a:
                a[i] = Node(i)
            a[i].count += 1
        return a
    
    
    def build_pq(l):
        q = queue.PriorityQueue()
        for i in l:
            q.put(i)
        return q
    
    
    def build_tree(q):
        while not q.empty():
            if q.qsize() == 1: return q.get()
            x, y = q.get(), q.get()
            q.put(Node('', x.count + y.count, x, y))
    
    
    def encode(tr, code):
        if not tr: return
        tr.code = code
        encode(tr.left, code + '0')
        encode(tr.right, code + '1')
    
    
    data = open("oceans.mp4", 'rb').read()
    a = cnt(data)
    q = build_pq(a.values())
    tr = build_tree(q)
    encode(tr, '')
    sz = sum([i.count * len(i.code) for i in a.values()])
    print('before', len(data)*8, '
    after', sz)
    
    
  • 相关阅读:
    [OpenCV] Ptr类模板
    [OpenCV]Mat类详解
    [C++] Vector用法
    [OpeCV] highgui头文件
    c++中的.hpp文件
    【2017】KK English
    CMake Tutorial & Example
    [g2o]C++图优化库
    Teradata基础教程中的数据库试验环境脚本
    Oracle中对象权限与系统权限revoke
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6218960.html
Copyright © 2011-2022 走看看