壓縮算法之Elias Gamma Coding & Elias Delta Coding
Posted by Andrew([email protected])
2013-02-24
基本概念
壓縮算法一般根據應用場景不同可分爲文本壓縮和索引壓縮,後者是搜索引擎的核心技術之一。壓縮的本質是對數據進行重新編碼,編碼依據是數據的分佈特性,一般是概率分佈情況,比如對於倒排索引中的數字編號。數值分佈一般符合指數分佈,均勻分佈,Elias Gamma/Delta主要是基於這兩種分佈的。
壓縮模型可分爲全局模型,局部模型,局部模型參數可動態調整,算法複雜度高,壓縮解壓縮速度慢,但壓縮率高。在編碼方式上可分爲定長和變長編碼。
Elias Gamma/Elias Delta算法適合小整數使用頻率較高的場景。它們都是基於數字分解的算法,都需要使用到下面兩種基本的編碼方式:
1.一元編碼-Unary
主要適合於指數分佈的數值分佈,對於數字N,一元編碼的編碼長度爲N,使用N-1個二進制1和末尾一個0表示,如數字3的一元編碼爲:110
2.二進制編碼-Binary
主要適合於均勻分佈的數值分佈,對於數字N的二進制編碼,其實就是它在計算機中的實際存儲格式。也就是十進制對應的二進制,如3的二進制爲11。在二進制編碼中,比特寬度會在具體的壓縮算法中不一樣,比如數字3如果用5個比特寬度則爲:00011
Elias Gamma Coding算法過程
1.對於數字x分解成 x=2N + M
2.對於N+1使用一元編碼
3.對於M使用比特寬度爲N的二進制編碼
舉例:比如對於數字13,可分解成:13 =23 + 5,即N=3,M=5,則N+1的一元編碼爲:1110;M的比特寬爲3的二進制編碼爲:101,最後的Elias Gamma Coding爲:1110:101.
Elias Delta Coding算法過程
該算法是在Gamma算法基礎上的改進,目的是爲了對N進一步壓縮,可以處理更大的數值。算法過程如下:
1.對於數字x分解成 x=2N + M
2.對於N+1使用進一步分解成N+1=2N1 + M1
2.1. 對N1+1使用一元編碼
2.2. 對M1使用比特寬度爲N1的二進制編碼
3.對於M使用比特寬度爲N的二進制編碼
可以看出,Delta Coding是將Gamma coding的第一個因子再進行了一次Gamma coding。
舉例:對於數字13,可分解成:13=23 + 5,即N=3,M=5,將N+1進一步分解成4=22 + 0,即N1=2,M2=0,所以第一部分編碼爲:110:00,第二部分編碼爲:101,最後的編碼爲:110:00:101.
以下是來自維基百科的Elias Gamma代碼片段,網址:http://en.wikipedia.org/wiki/Elias_gamma_coding
關於Elias Delta可參考:http://en.wikipedia.org/wiki/Elias_delta_coding
Encode
void eliasGammaEncode(char* source, char* dest) { IntReader intreader(source); BitWriter bitwriter(dest); while (intreader.hasLeft()) { int num = intreader.getInt(); int l = log2(num); for (int a=0; a < l; a++) bitwriter.putBit(false); //put 0s to indicate how many bits will follow bitwriter.putBit(true); //mark the end of the 0s for (int a=l-1; a >= 0; a--) //Write the bits as plain binary { if (num & 1 << a) bitwriter.putBit(true); else bitwriter.putBit(false); } } intreader.close(); bitwriter.close(); }
Decode
void eliasGammaDecode(char* source, char* dest) { BitReader bitreader(source); IntWriter intwriter(dest); while (bitreader.hasLeft()) { int numberBits = 0; while (!bitreader.getBit() && bitreader.hasLeft()) numberBits++; //keep on reading until we fetch a one... int current = 0; for (int a=numberBits-1; a >= 0; a--) //Read numberBits bits { if (bitreader.getBit()) current |= bitreader.getBit() << a; } current |= 1 << numberBits; //leading 1 isn't encoded! intwriter.putInt(current); } }