zigzag壓縮演算法
前文 Base 128 Varints 編碼(壓縮演算法) 介紹了Base 128 Varints這種對數字傳輸的編碼,瞭解到了這種編碼方式是為了最大程度壓縮數字的。但是,在前文裡,我們只談論到了正數的情況,那如果出現了負數,該怎麼辦?zigzag壓縮演算法解決的就是這個問題。
在聊這個演算法之前,我們得先補補課,聊聊二進位制補碼相關的東東。
一、二進位制及補碼
我們知道,計算機儲存的資料都是二進位制的01串,而數字的01串又是以補碼的形式儲存的,補碼是什麼東西?為什麼要用補碼?下面我們一個個來看:
1.原碼
要了解補碼,首先要了解原碼。
什麼是原碼?簡單點說,就是把十進位制轉為二進位制的表示形式,我們用第一個位表示符號(0為非負數,1為負數),剩下的位表示值。比如:
[+8] = [00001000]原
[-8] = [10001000]原
2.反碼
我們用第一位表示符號(0為非負數,1為負數),剩下的位,非負數保持不變,負數按位求反。比如:
[+8] = [00001000]原 = [0000 1000]反
[-8] = [10001000]原 = [1111 0111]反
就是說:正數的反碼是本身,負數的反碼保持符號位不變,其他位逐位取反
3.補碼
首先,我們來看為什麼會出現補碼,我們先來看2個問題
第一,0居然用2個編碼(+0和-0)來表示了:
原碼:[0000 0000]原 = [1000 0000]原
反碼:[0000 0000]反 = [1111 1111]反
第二,我們來看一個現象
1 + (-1)
= [00000001]原 + [1000 0001]原
= [10000010]原
= -2
明顯是不對的!
反碼:
1 + (-1)
= [00000001]反 + [1111 1110]反
= [1111 1111]反
= -0
表現的好詭異!(正常應該是 0000 0000)
為了解決這些問題,我們在計算機體系中引入了補碼。
現在我們來看補碼怎麼取:
我們用第一位表示符號(0為非負數,1為負數),剩下的位非負數保持不變,負數按位求反末位加一。
[+8] = [00001000]原 = [0000 1000]補
[-8] = [10001000]原 = [1111 0111]反 = [1111 1000]補
即:正數的補碼是自己本身,負數的補碼口訣為:逐位取反,末位加1
引進了補碼之後,我們看看計算的情況:
1 + (-1)
= [00000001]補 + [1111 1111]補
= [0000 0000]補
= 0
很明顯,通過這樣的方式,計算機進行運算的時候,就不用關心符號這個問題,而只需要按照統一的逢二進一的原則進行運算就可以了。
好了,補充了這些知識之後,可以進入正題了。
二、zigzag壓縮演算法
前文說到了,在絕大多數情況下,我們使用到的整數,往往是比較小的。然而,我們為了正確,又不得不把很多無用的0進行傳輸。Base 128 Varints編碼很好地解決了上面說的問題,但是,如果傳輸的數字出現了負數,會出現什麼情況呢?
依然舉個很簡單的例子,假設我要傳輸數字-1,會怎樣傳輸呢?
[-1]=[10000000 00000000 00000000 00000001]原 = [11111111 11111111 11111111 11111110]反 = [11111111 11111111 11111111 11111111]補
前面的廢物0,到這裡居然變成了廢物1了,怎麼樣才能進行壓縮呢?
zigzag給出了一個很巧的方法:我們之前講補碼講過,補碼的第一位是符號位,他阻礙了我們對於前導0的壓縮,那麼,我們就把這個符號位放到補碼的最後,其他位整體前移一位:
-1= [11111111_11111111_11111111_11111111]補= [11111111_11111111_11111111_11111111]符號後移
但是即使這樣,也是很難壓縮的,因為數字絕對值越小,他所含的前導1越多。於是,這個演算法就把負數的所有資料位按位求反,符號位保持不變,得到了這樣的整數:
-1= [11111111_11111111_11111111_11111111]補= [11111111_11111111_11111111_11111111]符號後移=[00000000_00000000_00000000_00000001]符號後移
我們熟悉的前面的一堆0又出現了,又可以愉快地使用Base 128 Varints演算法進行編碼了。
那麼,如果是正數的情況,這個演算法改怎麼處理呢?如果數字是正數,符號位一樣放到最後,其他資料全部保持不變就行了,例如:
[1]
= (00000000_00000000_00000000_00000001)補
= (00000000_00000000_00000000_00000010)符號後移
= (00000000_00000000_00000000_00000010)zigzag
這樣一弄,正數、0、負數都有同樣的表示方法了,我們就可以對小整數進行壓縮了