哈夫曼编码压缩解压缩实现不同类型文件压缩比的测试

本文主要是介绍哈夫曼编码压缩解压缩实现不同类型文件压缩比的测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

压缩原理及步骤&&压缩比的计算

压缩原理及步骤

压缩的第一步:

将一个文件以各个字符出现的次数为权值建立哈夫曼树,这样每个字符可以用从树根到该字符所在到叶子节点的路径来表示。(左为0,右为1)

压缩第二步:

哈夫曼编码有一个很重要的特性:每个字符编码不会成为另一个编码的前缀。这个特性保证了即使我们把不同长度的编码存在一起,仍然也可以把它们分离开,不会出现认错人的冲突。
那么我们就可以把所有的字符按照原有顺序用其编码替换,构建新的字符串作为其压缩后的串。

压缩第三步:

有的小伙伴可能要问了,这样一搞不是越变越多了么,哪是什么压缩。哈哈,大部分孩子可能已经想到啦,既然单位编码除了0就是1为什么还要用字节来存呢,用位来保存,8个单位编码为1位。这样转化完成后的串才是真正压缩后的串。

当然,因为我们还要进行解压,所以这里构建的树也要和串一并加入到文件。

压缩比的计算

介绍完步骤,我们来计算一下哈夫曼编码的压缩比。 用len表示串长度,path(i)表示每i个字符的编码长度,那么根据上文所介绍的原理,我们可以很容易知道,串通过哈夫曼压缩后的长度为
sum(path(i)) 1<=i<=len
这个式子虽然正确但不能直观的感受的压缩比,所以我们来假设一种平均情况进行估算 假如一个 串长度为n,一共包含 m个不同的字符,那么所构建成的哈夫曼树的 总结点数为 2*m-1。 假设,n很大,那么可以忽略树的保存所占用的空间。如果假设此串中每个字符出现的次数都是相同的,那么也可以假设,它们所生成的哈夫曼树是完全二叉树. 即每个叶子(字符)的 深度为log(m)+1,则路径长度为log(m)。log(m)即为该串字符的平均路径长度,那么压缩后的串长为log(m)/8。 由上可以得出平均压缩比的公式为:
n*log(2*m-1)/8/n = log(2*m-1)/8;
可见压缩比的大小主要与m有关,即不同的字符越少越好。 ascii码的范围为0~255,共有256种不同字符,代入上式得
log(2*256-1) = 6.23 …
向上取整为7(路径个数哪有小数)
7/8 = 0.875 = %87.5
所以哈夫曼编码的平均压缩比为%87.5。
强调

上述的假设在计算情况中忽略了对哈夫曼树的保存,所以只在文件总长度与不同字符总数相差很大时才生效。

考虑ascii码外的其它语言

一开始为考虑这个钻了牛角尖,想着去统一用wchar_t保存或是转为Unicode等等什么的。但其实不必那么复杂,因为汉字(不仅仅汉字,任何字符都是这样的)都是以字节为单位的,由多个字节组成的,将其分开对待,因为最终解压时恢复原串还是按照原有顺序组装,所以和纯英文文件的实现没有什么区别);

需要注意的地方

所有字符路径的总长不一定整除8,所以在按为保存时,要注意最后一项不足8的情况,进行补零,且要将补零的个数保存起来。

代码对不同类型文档的压缩比测试情况

英语文章
样例文档:西游记英文节选

原大小:7720
压缩后:10476
压缩比:1.356 – %135
此处的文件压缩后不降反增,因为文件本身大小与不同字符的数量相差并不大,加上对树的保存后,空间大于压缩前。

纯汉语文档

样例文档:西游记
原大小:1921978
压缩后:1781234
压缩比:0.926 – %92
不同汉字的数量多。

程序代码

样例文档:github网页源代码
原大小:46500
压缩后:35116
压缩比:0.755 – %76
源代码中全是英文字母与符号,不超过100种,总大小与其相差近500倍,且代码重复词比较多。

英语单词文档

样例文档:英语单词5000
原大小:20813
压缩后:13523
压缩比:0.649 – %65

测试情况

源代码

压缩程序源文件 compress.cpp

#include <iostream>
#include <locale>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <queue>using namespace std;const long long MAX_SIZE = 10000000000;//
const int MAX_TYPE = 300;
unsigned int *f = new unsigned int[MAX_TYPE];//计数
unsigned int *p = new unsigned int[MAX_TYPE];//计下标
char *v = new char[MAX_TYPE];
char filename[20];
char *s[MAX_TYPE];struct Node
{unsigned int weight, parent, lson, rson;Node(){};
}HuffmanTree[MAX_TYPE<<1];struct NodeCmp
{bool operator()(int a, int b){return HuffmanTree[a].weight > HuffmanTree[b].weight;}
};int CreatTree(char *str, long long len)
{int num = 1;for(int i=0;i<len;i++)f[str[i]]++;cout<<"len::"<<len<<endl;for(int i=0;i<len;i++){if(f[str[i]]){HuffmanTree[num].weight = f[str[i]];HuffmanTree[num].lson = 0;HuffmanTree[num].rson = 0;f[str[i]] = 0;if(p[str[i]] == 0)p[str[i]] = num;v[num] = str[i];++num;}}cout<<"num::"<<num<<endl;return num;
}void CodingTree(int num)
{priority_queue<int, vector<int>, NodeCmp> q;for(int i=1;i<num;i++)q.push(i);int len = num;for(int i=0;i<num-2;i++){int x = q.top(); q.pop();int y = q.top(); q.pop();HuffmanTree[len].weight = HuffmanTree[x].weight + HuffmanTree[y].weight;HuffmanTree[x].parent = HuffmanTree[y].parent = len;HuffmanTree[len].lson = y;HuffmanTree[len].rson = x;q.push(len++);}
}void FindPath(int num)
{char *t = new char[num];t[num-1] = '\0';for(int i=1;i<num;i++){int son = i, father = HuffmanTree[i].parent;int start = num-1;while(father != 0){--start;if(HuffmanTree[father].rson == son)t[start] = '1';elset[start] = '0';son = father;father = HuffmanTree[father].parent;}s[i] = new char[num - start];strcpy(s[i], &t[start]);}
}void print(int num, long long len, char *str)
{ofstream fout(filename, ios::out);fout<<num<<endl;for(int i=1;i<num;i++){fout<<s[i]<<endl;fout<<v[i]<<endl;}long long pos = 0;char *ans = new char[MAX_SIZE];int now = 7;for(long long i=0;i<len;i++){int k = 0;while(s[p[str[i]]][k] != '\0'){ans[pos] |= (s[p[str[i]]][k]-'0')<<now--;if(now < 0){now = 7;pos++;}++k;}}int zero = 0;if(now != 7) zero = now%7+1, pos++;fout<<zero<<" "<<pos<<endl;fout.write(ans, sizeof(char)*pos);fout.close();cout<<"zero::"<<zero<<endl;
}int main(int argc, char **argv)
{sprintf(filename, "%s.temp", argv[1]);ifstream fin(argv[1],ios::ate | ios::in);if(!fin){cout<<"File open error!"<<endl;return 0;}long long size = fin.tellg();if(size > MAX_SIZE){cout<<"Too long!"<<endl;return 0;}fin.seekg(0, ios::beg);char *str = new char[size+1];fin.read(str,size);fin.close();int num = CreatTree(str, size);CodingTree(num);FindPath(num);print(num, size, str);return 0;
}

解压程序源文件 compress.cpp

#include <iostream>
#include <locale>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <queue>using namespace std;
char filename[20];
const long long MAX_SIZE = 10000000000;//
const int MAX_TYPE = 300;
struct Node
{char v;int parent, lson, rson;Node(){};
}HuffmanTree[MAX_TYPE<<1];char *str = new char[MAX_SIZE];
char *ans = new char[MAX_SIZE];void CreatTree(char *t, char v, int &pos)
{int root = 0;for(int i=0;t[i]!='\0';i++){if(t[i] == '1'){if(HuffmanTree[root].rson == 0)HuffmanTree[root].rson = pos++;root = HuffmanTree[root].rson;}else{if(HuffmanTree[root].lson == 0)HuffmanTree[root].lson = pos++;root = HuffmanTree[root].lson;}}HuffmanTree[root].v = v;
}void print(int zero, int len, char *str)
{long long start = 0;int root = 0;int end = 0;for(int i=0;i<len;i++){char t = str[i];if(i == len-1)end = zero;for(int j=7;j>=end;j--){if((1<<j) & t)root = HuffmanTree[root].rson;elseroot = HuffmanTree[root].lson;if(HuffmanTree[root].lson == 0 && HuffmanTree[root].rson == 0){ans[start++] = HuffmanTree[root].v;root = 0;}}}cout<<"len::"<<start<<endl;ofstream out(filename, ios::out);out.write(ans, sizeof(char)*(start));out.close();
}int main(int argc, char **argv)
{strcpy(filename, argv[1]);filename[strlen(filename)-4] = 'o';filename[strlen(filename)-3] = 'u';filename[strlen(filename)-2] = 't';filename[strlen(filename)-1] = '\0';ifstream fin(argv[1], ios::in);if(!fin){cout<<"File open error!"<<endl;return 0;}int num;char *t = new char[num];char *v = new char[3];fin>>num;fin.getline(t,num);cout<<"size::"<<num<<endl;int pos = 1;for(int i=1;i<num;i++){fin.getline(t,num);fin.getline(v,num);if(v[0] == '\0'){fin.getline(v,num);v[0] = '\n';    }CreatTree(t, v[0], pos);v[0]=0;}int zero;long long size;fin>>zero; fin>>size;fin.getline(t,num);fin.read(str,sizeof(char)*size);print(zero, size, str);cout<<"zero::"<<zero<<endl;return 0;
}

代码读写操作用文件流实现,所以在时间效率方面还有很多可优化的地方,待日后闲了再说,毕竟考试在即。。。如果哪里有错误,欢迎砸砖,便于在下提升修正。

这篇关于哈夫曼编码压缩解压缩实现不同类型文件压缩比的测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920244

相关文章

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主