实验任务(实验题目、目的)
Implementing a data compressing program using Huffman coding.
利用Huffman树和Huffman编码实现一个无损数据压缩软件工具。
任务分析
实现思路
首先,对频率进行统计
def WordCounting(words):
wordset={}
lis=[]
for word in words:
if word in wordset:
wordset[word]=wordset[word]+1
else:
wordset[word]=1
for key,value in wordset.items():
temp=node(value=key,freq=value)
lis.append(temp)
return lis
再利用排序算法实现哈夫曼编码,无需递归
def encodeList(data):
lis={}
completed=[]
todo=data
while len(todo)!=1:
todo=sorted(todo,key=lambda k:k.freq,reverse=True)
a=todo.pop()
b=todo.pop()
a.code=a.code+"0" if a.freq<=b.freq else "1"
b.code = b.code + "0" if a.freq > b.freq else "1"
completed.append(a)
completed.append(b)
father=node(freq=a.freq+b.freq)
a.father=father
b.father=father
todo.append(father)
completed.append(todo.pop())
completed.reverse()
minlen=100
maxlen=0
for n in completed:
if n.father != None:
n.code =n.code+n.father.code
if n.value!=-1:
lis[n.value]=n.code[::-1]
minlen=min(len(lis[n.value]),minlen)
maxlen = max(len(lis[n.value]), maxlen)
return lis,[minlen,maxlen]
根据加密字典对原文进行压缩
def Encode(data,encodelist):
new=""
for d in data:
new=new+encodelist[d]
return new
最后保存文件为十六进制,因为无法直接写入磁盘,写入十六进制能节约空间
def SaveZip(zip,path):
code=[]
num=0
while True:
temp=hex(int(zip[num:num+8],2))
num=num+8
code.append(temp)
if len(zip)-num<8:
break
temp = hex(int(zip[num:], 2))
code.append(temp)
break
# print(code)
with open(path, "wb") as f:
for li in code:
s = struct.pack('B',int(li,16))
f.write(s)
f.close()
对原文进行解压缩
def Decode(encodelist,data,ra):
minlen, maxlen=ra[0],ra[1]
code=copy.copy(data)
code=code.hex()
s=str(bin(int(code,16)))
s=s[2:]
index=0
text=""
while True:
for i in range(minlen,maxlen+1):
temp=s[index:index+i]
if temp in encodelist.keys():
text=text+encodelist[temp]
index=i+index
break
if index>=len(data):
break
return text
涉及的知识点
基于贪心策略的压缩—哈夫曼编码:通过利用哈法码编码算法实现了对文件的压缩。
哈夫曼树(Huffman Tree)也是一种特殊的二叉树,这种树的所有叶子结点都带有权值,从中构造出带权路径长度最短的二叉树,即哈夫曼树。
哈夫曼树的定义:设二叉树具有n个带权值的叶子结点,那么从根结点到各个叶子结点的路径长度与相应结点权值的乘积的和,叫做二叉树的带权路径长度,记作:
其中,为第i个叶子结点的权值,l为第i个叶子结点的路径长度。如图6.19所示的二叉树, 它的带权路径长度值WPL=1×3+3×3+2×2+4×1=20
如果给定一组具有确定权值的叶子结点· 可以构造出不同的带权二叉树, 它们的带权路径长度并不相同· 我们把其中具有最小带权路径长度的二叉树称为哈夫曼树。
哈夫曼树的构造
哈夫曼编码
哈夫曼编码具有广泛的应用, 利用哈夫曼树构造的用于通信的二进制编码称为哈夫曼编码。例如: 有一段电文“ CAST囗TAT囗A囗SA "( 其中,“ 囗” 表示一个空格) 。统计电文中字母的频度 f(‘C’)=1,f(‘S’)=2,f(‘T’)=3,f(‘囗’)=3,f(‘A’)=4 。
用频度{ 1 , 2 , 3 , 3 , 4 } 为权值生成哈夫曼树. 并在每个叶子上注明对应的字符。树中从根到每个叶子都有一条路径, 对路径上的各分枝约定指向左子树根的分枝表示“ 0 ” 码, 指向右子树的分枝表示“ 1 ” 码, 取每条路径上的“ 0 ” 或“ 1 ” 的序列作为和各个叶子对应的字符的编码, 这就是哈夫曼编码。对应图6- 22 的哈夫曼树,上述字符编码为:
信源熵:在信息论中,熵(英语:entropy)是接收的每条消息中包含的信息的平均量,又被称为信息熵、信源熵、平均自信息量。这里,“消息”代表来自分布或数据流中的事件、样本或特征。(熵最好理解为不确定性的量度而不是确定性的量度,因为越随机的信源的熵越大。)来自信源的另一个特征是样本的概率分布。这里的想法是,比较不可能发生的事情,当它发生了,会提供更多的信息。由于一些其他的原因,把信息(熵)定义为概率分布的对数的相反数是有道理的。事件的概率分布和每个事件的信息量构成了一个随机变量,这个随机变量的均值(即期望)就是这个分布产生的信息量的平均值(即熵)。熵的单位通常为比特,但也用Sh、nat、Hart计量,取决于定义用到对数的底。
采用概率分布的对数作为信息的量度的原因是其可加性。例如,投掷一次硬币提供了1 Sh的信息,而掷m次就为m位。更一般地,你需要用log2(n)位来表示一个可以取n个值的变量。在1948年,克劳德·艾尔伍德·香农将热力学的熵,引入到信息论,因此它又被称为香农熵。
熵的计算:当取自有限的样本时,熵的公式可以表示为:在这里b是对数所使用的底,通常是2,自然常数e,或是10。当b = 2,熵的单位是bit;当b = e,熵的单位是nat;而当b = 10,熵的单位是Hart。
出现的问题
无法直接将生成的01数据写入磁盘,而必须保存为UTF-8这样的文件。
压缩结果