说句大实话,那会儿学编码就当作那是数学题,非得把字母拆成小数点,再一个个往后推,直到算出那个唯一的整数。目前回头想想,那玩意儿实际上是概率在偷懒。想象你手里有一堆石头,有的重有的轻。你不用去秤每一块,只要记住哪块最轻,给它放个最小的箱子;哪块最重,就给它配个最大的袋子。
反正不管你如何装,总体的体积不会变,只是让最重的东西略微占点空间,最轻的刚好塞进角落。
这就是 Huffman 编码的核心,本质就是给出现象时概率小的那些事,找一个略微“贵点”的盒子,而概率大的直接用大箱子,反正概率小的东西数量本来就少,占个空间不算事儿。 别总想着去优化这俩字母表,那是给白痴设计的。哈夫曼树这玩意儿,实际上是堆出来的。你先把所有可能的字符串概率算出来,然后按从小到大排个序,哪位排在前面哪位先干活。
这时候你就得动手,哪位跟哪位挤一挤,哪位跟哪位挤一挤,最终形成一个像树一样结实的结构。最上面那个头叫根,下面分叉的左右两棵子树,它们各自也分叉,一层层往下挖,直到最终只剩下一根绳子连到地上。
这根绳子就是你的编码方案,哪位在这棵树的左边走,哪位就加个"0",往右走就加个"1"。
这个0和1不是随意加的,是出于你往哪边走,概率大的东西就少走几步,概率小的东西就得多走几步。
这就像人走楼梯,走楼梯的人多,走几步就够;走地毯的人少,走整个个路才够。概率小的东西走几步就够,概率大的东西得走大半截,这样最节省资源。 拿个具体的例子你就懂了,比如你手里有四个段子:A 段子要讲三次,B 段子要讲一次。A 段子概率是 3/4,B 段子概率是 1/4。你要是按字母顺序排,A 和 B 哪位先哪位后实际上无所谓,但你得让它们挤在一起。A 和 B 挤在一起,就形成 ABA 要么 BAB 的树形。
这时候 A 走几步,B 如何走?A 是长个子,B 是短个子。B 要是走左子树,那 A 就务必走右子树。结局出来了,A 段子用了 3 次,每次加个 0,变成 "000";B 段子用了 1 次,加个 1,变成 "1"。
你看,概率小的 B 走了 1 步,概率大的 A 走了 3 步。
要是反过来,A 走右子树,B 走左子树,那 B 就得走 3 步,A 走 1 步。结局就是错的,概率小的东西不能走忒远,务必走最近的。哈夫曼树的构造过程就是这个逻辑的具象化,它强行让概率小的东西走近,概率大的东西走远,反正总长度不增添,只是重新分配了位置罢了。 再细说那层级的分叉,别总说“根节点”,根节点只是个集合,集合里包含两棵子树。每棵子树里又包着两棵子树,直到最终只剩下一根线。
这时候你就要招人干活了,哪位招人?概率小的东西多,就招人。出于概率小的东西数量多,故此它们组成的树就大,树就深,树就大,树就占地大。树大,树就深,树就大,树就占地大,树就占地多。概率大的东西就少招人,树就小,树就浅,树就占地少。
故此树的大小,实际上是由树中概率小的东西的数量拍板的。概率小的东西多,树就大,树就深,树就占地多。概率大的东西少,树就小,树就浅,树就占地少。哈夫曼编码就是如此照顾这些“贪吃草”的,它们多,树就大,它们少,树就小。树枝长一点,树枝长一点,这样概率小的东西就能多走几步,概率大的东西就能少走几步。
反正总长度不变,只是哪位多走哪位少走罢了。 关于编码长度,这东西实际上挺好办理解的。编码长度等于叶子节点深度加一。叶子节点深度就是距离根节点有几层,每层代表一次编码。深度多,编码长;深度浅,编码短。概率小的东西深度多,编码长;概率大的东西深度浅,编码短。
比如刚刚那四个段子,A 段子概率最大,故此它在树的底部,深度才 3 层,编码就是 3 位。B 段子概率最小,它在树的顶部,深度才 1 层,编码就是 1 位。
要是你让 A 的深度变成 2 层,B 的深度变成 3 层,那 A 就得加个 00,B 就得加个 000。结局就是编码变长了,总长度变多了。
故此哈夫曼编码极力让概率小的东西深度增添,让概率大的东西深度削减。
这就像你在玩扔石头的游戏,你扔的次数少,石头就大;你扔的次数多,石头就小。概率小的东西扔的次数少,它就占个大位置;概率大的东西扔的次数多,它就占个小位置。 最终说下潜在的难题,有时候你会想,万一概率都一样如何办?这时候哈夫曼树也得演变成平衡树,所有叶子都在同一层,深度都一样。
这时候编码长度就固定了,没法再优化了。但要是概率分布特别极端,比如 99% 的概率都是 A,只有 1% 的概率是 B,这时候哈夫曼编码会让 A 的深度变成 2 层,B 的深度变成 2 层,结局 B 的编码变长了。
不过这种情况在现实里极少见,实际应用中概率分布都有个中间态,既不忒极端,也不忒均匀,哈夫曼编码能挺好地适配。并且有时候你会发现,别看理论上的编码长度最短,但实际解码时,那些概率挺小的东西到底能解码出来吗?有时候解码器会报错,这时候哈夫曼编码就失效了,得换个别的编码方案。但这恰恰也说明白哈夫曼编码的局限性,它不是万能的,它只是在概率分布可控的情况下,能拿到最优长度的解。
故此别死磕哈夫曼编码,有时候换个思路,要么干脆用固定长度的编码,说不定更实用。