信息論II:最優二叉樹與Huffman編碼

本期內容一覽:


  1. JSON的“噪音”與“信噪比”

  2. 噪音量的理論上限、逆波蘭表達式

  3. 信息論與壓縮技術:字符串vs字節串

  4. 最優二叉樹、FPS/2.0

  5. Huffman編碼

  6. Message Pack

  7. Message Pack 的 Huffman 樹

  8. 前綴 VS 分隔符

  9. Message Pack 缺陷、宿主環境Bug

  10. 序列化的極限、兩個基本公理

  11. UTF-8極限壓縮

  12. 有理數:變長類型偏移術

  13. 字典壓縮大法

  14. 尾部殘缺問題

  15. Ultra Pack與時空置換原理

  16. V8引擎玄學(What's happening under the hood)


 這裏不談技術,只談思想。

一些廢話。

本來這份ppt是打算在公司的FEConf大會上展示的,但是年初的新型冠狀病毒疫情把這事兒給鴿了。話說16XX年春天,倫敦地區也爆發了一場慘絕人寰的鼠疫,然後牛頓大神在家隔離時宅出了包括二項式定理和微積分在內的一系列頂級學術成果,進而導致了人類第一次理論物理大爆發...

如今人類的理論物理已經將近100年毫無進展了,所有的應用還基於上世紀初的相對論&量子力學。於是我等社畜在微博和朋友圈“理論儲備告急”運動的鼓勵下也試圖利用在家隔離的時間裝模做樣的搞搞科學,尤其是脫離了應用趣味的理論研究。

我研究的方向是香農的信息論(是從B站上進化論專區上過來的,據說進化論基於信息論????),想着能對信息、熵、生命有更好的理解,通過學習來降低自己的信息熵,關於信息與熵的關係,參考上一篇文章《信息與熵【上】生命以信息爲食》。本文是系列第二篇,主題是關於信息論中的信息壓縮算法,將年前準備在公司演講用的ppt完整地翻譯成一篇文章,內容更多的回到了計算機軟件上。

01

JSON的噪音


主題:尋找序列化的極限。

什麼是序列化?序列化是一種將多維度的數據無損地轉換成一維的線性數據,是一種編碼手段,之所以要轉換成一維是爲了更好地存儲和傳輸,關於序列化的知識參考這篇文章

信息論認爲,“編碼”是信息從一種形式或格式轉換爲另一種形式的過程,比如,字符編碼就是將文本格式的信息轉換成字節碼格式,反之,base64編碼是將字節碼格式轉換成文本格式。“解碼”是編碼的逆過程。

名詞解釋:編碼

那JSON就是這樣一種流行的序列化格式,JSON支持實數、字符串、布爾、null這4個基本類型和列表、字典這2個複合類型,非常好用的同時當然也是有缺點的,JSON的“信噪比”很低,但具體是多少很難說。

根據信息論的觀點,數據 = 信息 + 噪音。這個理論放在文本型序列化格式中的公式就是:JSON = 類型位 + 信息位。其中信息位就是json含有的有效信息量,類型位則是剩下所有噪音,包括雙引號,逗號,方括號,花括號等。

json中的噪音是可壓縮的,舉一些一眼就能看出來的可優化之處:如果把鍵值對中“鍵”的雙引號去掉,把true和false用字母t和f來代替,也不會產生歧義。

還有更高端的玩法:把json裏所有的括號換成逆波蘭表達式。也是一種有效減少體積的方式。

除此之外,實數類型是用十進制字符的形式存儲的,這不僅造成了許多噪音,還增加了進制轉換的時間。

再仔細找還能發現json許多多餘的數據,可以一直不斷的被壓縮,但這個壓縮的極限在哪裏?一定是有極限的,json不可能被無限壓縮。

02

數據壓縮有極限嗎?

西班牙有個叫González的老哥設計了一個json的壓縮算法,也是基於文本的,據說能將嵌套很深的json壓縮至55%,比如有下面這樣的一個json:

{
    "type": "world",
    "name": "earth",
    "children": [
        {
            "type": "continent",
            "name": "America",
            "children": [
                {
                    "type": "country",
                    "name": "Chile",
                    "children": [
                        {
                            "type": "commune",
                            "name": "Antofagasta"
                        }
                    ]
                }
            ]
        },
        {
            "type": "continent",
            "name": "Europe"
        }
    ]
}

通過老哥的“壓縮算法”得到的這樣一串:

type|world|name|earth|children|continent|America|country|Chile|commune|Antofagasta|Europe^^^$0|1|2|3|4|@$0|5|2|6|4|@$0|7|2|8|4|@$0|9|2|A]]]]]|$0|5|2|B]]]

而且壓縮率驚人,甚至超過了之後要說的MessagePack,但是仔細研究便發現,它其實是利用了人們對json的使用習慣來壓縮的,比如人們經常使用TypedArray(有類型列表),像json-schema一樣限定列表內對象的屬性,González老哥正是將經常出現的相同鍵名比如name、id、children這些,把他們收集起來使用,才降低了耦合度,壓縮了體積。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

TypedArray介紹

但這種利用習慣的壓縮算法畢竟是不持久的,因爲習慣會變化,所以並不推薦使用。在特定場合下,或者習慣固定的一個團隊內,這種算法尚有用武之地。

但不能說今天用這個格式,明天又想到一個更好的壓縮算法,再換新格式,這個“更好”到底是好多少,如何定量,以及上限是多少,這些纔是信息論關心的。爲了尋找這個極限,就得研究信息的“根本”格式:二進制格式;而不是基於二進制格式的文本格式。在文本格式上改良優化永遠無法達到信息壓縮的極限。所以序列化格式進化歷程中的核心矛盾是:文本格式vs數字格式。

言而總之,文本格式是時代發展中一種”偷懶”的過渡格式。正因爲文本格式簡單易擴展的特點,早期的許多序列化格式,http,甚至ip協議族都有很多還保留着文本格式。尤其是佔據9成以上互聯網流量的http/1.X協議,由於http/1.1的頭部和body(json)全都是文本格式,http也遇到了性能瓶頸:從時間上看,http文本編譯與解析浪費時間;從空間上看,http頭部大量的字段浪費空間。

03


信息論與壓縮技術

但是從http/2.0開始(以下簡稱h2),這種不良風氣開始改變了,對於head和body,h2採用了不同的壓縮算法來提高效率。對於靜態的header,h2採用定長編碼來壓縮,也就是爲每個常用的頭部如content-type:text/html分配一個固定的編號表示。對於動態的head,可自定義的head和value值,h2則採用的是對ASCII的變長編碼:Huffman編碼。

至此,http的head部分已經全部數字化了,body部分由於是用戶自定義的,仍然保留着json這個文本格式,但是w3c呼籲大家使用json的替代品,但沒有直說是哪種替代品,讓我們自由的去選擇二進制序列化格式。

注:文本格式變成二進制格式的過程叫做“數字化”,因爲二進制格式更像是一種“數字格式”。

04


最優二叉樹

既然w3c推薦我們使用Huffman編碼的二進制序列化格式,很有必要去認識一下Huffman的數據結構:最優二叉樹。

設計一套編碼最直接的方式是定長編碼,就是每種類型/字符的長度一定,比如ASCII編碼定長的8bit。如圖,用定長編碼設計5種字符至少需要3bit的編碼長度,圖中的深度爲3的滿二叉樹的每一片葉子都是一個字符,但剩下3個葉子因沒用到而浪費了。

這時候可以將使用頻率最高的一個字符的長度從3壓縮至1。這樣做的意義是犧牲那些無意義的編碼的數量來節省有意義編碼的長度。給使用頻率最高的那個字符賦予1bit的編碼長度後就生成了一個最優二叉樹。

05


Huffman編碼

這個“最優二叉樹編碼”其實就是“Huffman編碼”的同義詞。Huffman編碼是變長編碼,即每個編碼對象的長度不一樣,但是任意排列組合起來不會產生歧義。但是從定長編碼轉向變長編碼的代價是:體積增加(葉子的總深度損失)。所以,所有對象使用頻率一定(或者頻率無法預知)的情況下,使用定長編碼的效率更高,這有一點“周長一定,正方形面積最大”的意思。

當然,Huffman樹本身的生成算法是根據不同對象的使用頻率,從樹葉收斂到樹根,使得二叉樹最優,算法細節略。

<未完待續>


下集預告:《尋找序列化的極限》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章