[计算机基础] 什么是哈夫曼编码

What is Huffman Coding?

The Huffman Coding algorithm is a building block of many compression algorithms, such as DEFLATE - which is used by the PNG image format and GZIP.

什么是哈夫曼编码?

哈夫曼编码算法是很多压缩算法的基础,如PNG图片和GZIP使用的DEFLATE算法。

Why should I care?

Have you ever wanted to know:

  • How do we compress something, without losing any data?
  • Why do some things compress better than others?
  • How does GZIP work?

为什么关注哈夫曼编码?

你是否想知道:

  • 如何不丢失数据进行压缩?
  • 为什么有些东西比其他压缩的好?
  • GZIP是如何工作的?

In 5 minutes or less:

Suppose we want to compress a string (Huffman coding can be used with any data, but strings makes good examples).

Inevitably, some characters will appear more often than others in the text to be compressed. Huffman Coding takes advantage of that fact, and encodes the text so that the most used characters take up less space than the less common ones.

5分钟或更少的时间(阅读下文所需时间)

假设我们想压缩一个字符串(哈夫曼编码可以被用来压缩任何数据,不过字符串可以用来很好的举例)。

在字符串中,一些字符不可避免的比其他字符出现的次数更多。哈夫曼编码利用了这一特点,使频繁出现的字符相比很少出现的字符占用更少的空间。

Encoding a string

Let’s use Huffman Encoding to compress a (partial) quote from Yoda; “do or do not”.

“do or do not” is 12 characters long. It has a few duplicate characters, so it should compress quite nicely.

For the sake of argument, we’ll assume that storing each character takes 8 bits (Character Encoding is another topic entirely). This sentence would cost us 96 bits, but we can do better with Huffman coding!

We begin by building a tree structure. The most common characters in our data will be closer to the root of the tree, while the nodes furthest away from the root represent the less common characters.

Here’s the Huffman Tree for the string “do or do not”:

编码一个字符串

让我们用哈夫曼编码一个字符串“do or do not”。

“do or do not”是一个有12字符的字符串。它有一些重复的字符,因此它可以被很好的压缩。

我们假设每个字符占用8位,因此这个字符串共占用96位。

我们首先建立一个树结构。字符串中出现频率越高的字符会越靠近树根,出现频率越低的字符越远离树根。

如下是对“do or do not”的哈夫曼树:
在这里插入图片描述

The most common characters in our string are ‘o’s (4 occurrences) and spaces (3 occurrences). Notice that the path to those characters is only two steps away from the root, compared to three for the least common character (’t’).

Now, instead of storing the character itself, we can store the path to the character instead.

We start at the root node and work our way down the tree towards the character we want to encode. We’ll store a 0 if we take the left-hand path, and a 1 if we take the right.

Here’s how we would encode the first character, d, using this tree:

字符串中最频繁出现的字符是‘o’(出现4次)和空格(出现3次)。从树根除非仅需两步即可到达这两个字符,而到字符‘t’(出现1次)则需要3步。

现在,我们可以不存储字符本身,而是存储这个字符在树中的路径。

我们从树根出发,一直走到我们想要编码的字符(树叶)。如果我们往左走,则用0表示,如果往右走,用1表示。下图展示了如何使用哈夫曼树去编码第一个字符d
在这里插入图片描述

The end result is 1 0 0 - 3 bits instead of 8. That’s quite an improvement!

The entire encoded string looks like this:

最终字符d的结果是1 0 0,3位表示而不是一开始的8位。这是一个很大的提升!

完整的字符串编码如下:
在这里插入图片描述

This is 29 bits instead of 96, with no data loss. Great!

这一共使用了29位,远小于96位!并且没有丢失数据!

Decoding our string

To decode our text, we simply follow each 0 (left branch) or 1 (right branch) until we reach a character. We write that character down, and then start again from the top:

解码该字符串

解码该字符串,我们仅需要按照路径(如1 0 0)从根节点在树中去寻找对应的字符(如d)。每次找到一个字符,再从树根重新寻找。
在这里插入图片描述

Sending the encoded text

But wait… when we send the encoded text to somebody else, don’t they need the tree too? Yes! The other side needs the same Huffman tree in order to decode the text correctly.

The simplest, but least efficient way, is to simply send the tree along with the compressed text.

We could also agree on a tree first, and both use that tree when encoding or decoding any string. That works OK when we can predict the distribution of characters ahead of time, and could build a relatively efficient tree without seeing the specific thing we’re encoding first (as we could with English text, for example).

Another option is to send just enough information to allow the other side to build the same tree as us (this is how GZIP works). We could send the total number of times that each character occurs, for example. We have to be careful though; there is more than one possible Huffman tree for the same block of text, so we have to make sure we both construct the tree in exactly the same way.

发送编码数据

当我们发送用哈夫曼编码的数据时,是否也需要发送哈夫曼树?答案是肯定的。接收数据的一方需要使用相同的哈夫曼树进行正确解码。

不过发送数据的同时发送对应的哈夫曼树时最简单也最低效的方法。

因此,收发双方可以事先约定一个哈夫曼树用来编解码。但这仅限于我们可以预知待编码数据的字符分布(字符在字符串中的出现频率)。

另一个方法是仅发送构建相同哈夫曼树的基本信息(GZIP便采用此方法)。如我们可以发送每个字符出现的次数,对端可以据此构建哈夫曼树进行解码。但是需要特别注意,相同的字符出现次数可能构造出多个不同的哈夫曼,这会导致解码数据与原始数据不一致,因此必须保证收发双方的哈夫曼树相同。

Want to know more?

Check out these links:

拓展内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值