概述
在之前的文章中,我們知道了如何壓縮一段重複元素組成的資料。這種壓縮稱為“遊程編碼”,該演算法在無損資料壓縮傳輸時非常方便。但問題是資料必須遵循特定格式。比如,字串“aaaaaaaabbbbbbbb”可以被壓縮成“a8b8”。此時,16個字元的字串被壓縮成4個字元,沒有丟失任何資訊,而長度卻只有原始長度的25%。但當字元(元素)以不同方式分散時,問題就會出現。如果字元不變,但是沒有連續出現,會是什麼情況?如果字串是“abababababababab”會如何?長度一樣,字元一樣,但是我們不能使用遊程編碼!確實,使用遊程演算法在最優情況下只能得到相同的字串。
然而在這種情況下,我們看到另一個事實。該字串有太多重複元素組成,儘管不是一個接著另一個。我可以使用點陣圖壓縮該字串。也就是說我們可以使用序列中的位來儲存給定元素出現的位置,這個序列可以簡單地轉換成一個十進位制值。上例中的字串“abababababababab”可以壓縮成“1010101010101010”,即十進位制數43690,甚至表示成十六進位制的AAAA更好。由此這個長字串就被壓縮了。當解壓(解碼)訊息時,我們再從十進位制/十六進位制轉化成二進位制,匹配字元的出現次數。當然,上面這個例程非常簡單,假設只有一個重複字元,其餘組成字元不同,像這樣:“abacadaeafagahai”。那麼,我們可以使用對字元“a”使用點陣圖-“1010101010101010”,壓縮後為“AAAA bcdefghi”。正如你所看到的,所有例子字串只有16字元,這是一個限制。對變長資料使用點陣圖有些棘手,它的解碼不太容易。
從根本上來說,點陣圖壓縮儲存了訊息中頻繁出現元素的位置!
此外,點陣圖壓縮不僅適用於字串。也能壓縮陣列,物件以及任何資料。我之前帖子中的例程就很合適。我們需要使用JSON從伺服器傳輸一個很大的陣列到客戶機(瀏覽器)。該資料非常適合於“遊程編碼”。假設資料不一樣——不同年份的集合,這些時間以不同方式分散。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$data = array( 0 => 1991, 1 => 1992, 2 => 1993, 3 => 1994, 4 => 1991, 5 => 1992, 6 => 1993, 7 => 1992, 8 => 1991, 9 => 1991, 10 => 1991, 11 => 1992, 12 => 1992, 13 => 1991, 14 => 1991, 15 => 1992, ... ); |
JSON將會對訊息進行編碼,編碼後的訊息如下(一個簡單但很大的javascript陣列)。
1 |
[1991,1992,1993,1994,1991,1992,1993,1992,1991,1991,1991,1992,1992,1991,1991,1992, ...] |
然而,如果使用點陣圖壓縮,我們將得到一個更短的陣列。
1 2 3 4 5 6 |
$data = array( 0 => array(1991, '1000100011100110'), 1 => array(1992, '0100010100011001'), 2 => array(1993, '0010001000000000'), 3 => array(1994, '0001000000000000'), ); |
此時的JSON如下:
1 |
[[1991,"1000100011100110"],[1992,"0100010100011001"],[1993,"0010001000000000"],[1994,"0001000000000000"]] |
很明顯,隨著待壓縮資料增加,壓縮率會變得越來越好。事實上,大部分人都是從影象瞭解了點陣圖壓縮,因為該演算法主要用於影象壓縮。可想而知,在壓縮黑白影象時效果會多麼好(因為黑色和白色可以視為0和1)。實際上,它也被用於超過兩種顏色(例如256色),壓縮的程度就非常高。
實現
下面基於PHP的實現僅僅是為了闡明點陣圖壓縮演算法。這個演算法適用於任何資料結構。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// too many repeating "a" characters $msg = 'aazahalavaatalawacamaahakafaaaqaaaiauaacaaxaauaxaaaaaapaayatagaaoafaawayazavaaaazaaabararaaaaakakaaqaarazacajaazavanazaaaeanaaoajauaaaaaxalaraaapabataaavaaab'; function bitmap($message) { $i = 0; $bits = $rest = ''; while ($v = $message[$i]) { if ($v == 'a') { $bits .= '1'; } else { $bits .= '0'; $rest .= $v; } $i++; } return number_format(bindec($bits), 0, '.', '') . $rest;; } echo bitmap($msg); // uncompressed: acaaaaadaaaabalaaeaaaaganaaxakaavawamaasavajawaaaayaauaaadalanagaeaeamaarafalaazaaaiasaanaahaaazaraxaalaahaaawaaajasamahaajaakarapanaakaoakaanawalaacamauaamaal // compressed: 152299251941730035874325065523548237677352452096zhlvtlwcmhkfqiucxuxpytgofwyzvzbrrkkqrzcjzvnzenojuxlrpbtvb |
應用
當資料中有元素頻繁出現時,該演算法效果很好,所以你需要研究待壓縮資料的本質。實際上因為這個原因,該演算法通常用於PNG8或GIF影象的壓縮。