[算法系列之十七]数据压缩之位图

概述

在之前的文章([算法系列之十六]数据压缩之游程编码)中,我们知道了如何压缩一段重复元素组成的数据。这种压缩称为“游程编码”,该算法在无损数据压缩传输时非常方便。但问题是数据必须遵循特定格式。比如,字符串“aaaaaaaabbbbbbbb”可以被压缩成“a8b8”。此时,16个字符的字符串被压缩成4个字符,没有丢失任何信息,而长度却只有原始长度的25%。但当字符(元素)以不同方式分散时,问题就会出现。如果字符不变,但是没有连续出现,会是什么情况?如果字符串是“abababababababab”会如何?长度一样,字符一样,但是我们不能使用游程编码!确实,使用游程算法在最优情况下只能得到相同的字符串。

然而在这种情况下,我们看到另一个事实。该字符串有太多重复元素组成,尽管不是一个接着另一个。我可以使用位图压缩该字符串。也就是说我们可以使用序列中的位来保存给定元素出现的位置,这个序列可以简单地转换成一个十进制值。上例中的字符串“abababababababab”可以压缩成“1010101010101010”,即十进制数43690,甚至表示成十六进制的AAAA更好。由此这个长字符串就被压缩了。当解压(解码)消息时,我们再从十进制/十六进制转化成二进制,匹配字符的出现次数。当然,上面这个例程非常简单,假设只有一个重复字符,其余组成字符不同,像这样:“abacadaeafagahai”。那么,我们可以使用对字符“a”使用位图-“1010101010101010”,压缩后为“AAAA bcdefghi”。正如你所看到的,所有例子字符串只有16字符,这是一个限制。对变长数据使用位图有些棘手,它的解码不太容易。


从根本上来说,位图压缩保存了消息中频繁出现元素的位置!

此外,位图压缩不仅适用于字符串。也能压缩数组,对象以及任何数据。我之前帖子中的例程就很合适。我们需要使用JSON从服务器传输一个很大的数组到客户机(浏览器)。该数据非常适合于“游程编码”。假设数据不一样——不同年份的集合,这些时间以不同方式分散。

$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数组)。

[1991,1992,1993,1994,1991,1992,1993,1992,1991,1991,1991,1992,1992,1991,1991,1992, ...]

然而,如果使用位图压缩,我们将得到一个更短的数组。

$data = array(
        0 => array(1991, '1000100011100110'),
        1 => array(1992, '0100010100011001'),
        2 => array(1993, '0010001000000000'),
        3 => array(1994, '0001000000000000'),
);

此时的JSON如下:

[[1991,"1000100011100110"],[1992,"0100010100011001"],[1993,"0010001000000000"],[1994,"0001000000000000"]]

很明显,随着待压缩数据增加,压缩率会变得越来越好。事实上,大部分人都是从图像了解了位图压缩,因为该算法主要用于图像压缩。可想而知,在压缩黑白图像时效果会多么好(因为黑色和白色可以视为0和1)。实际上,它也被用于超过两种颜色(例如256色),压缩的程度就非常高。

实现

下面基于PHP的实现仅仅是为了阐明位图压缩算法。这个算法适用于任何数据结构。

// 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图像的压缩。

原文链接

Computer Algorithms: Data Compression with Bitmaps

时间: 2024-10-30 00:17:19

[算法系列之十七]数据压缩之位图的相关文章

算法系列(十七) 日历生成算法-中国公历(格里历)(下)

[接上篇] 上述计算星期的方法虽然步骤简单,但是每次都要计算两个日期的时间差,不是非 常方便.如果能够有一个公式可以直接根据日期计算出对应的星期岂不是更好?幸运的是,这样的公式是 存在的.此类公式的推导原理仍然是通过两个日期的时间差来计算星期,只是通过选择一个特殊的日期来 简化公式的推导.这个所谓的特殊日期指的是某一年的12月31日这天刚好是星期日这种情况.选择这样的 日子有两个好处,一个是计算上可以省去计算标准日期这一年的剩余天数,另一个是计算出来的日期差余 数是几就是星期几,不需要再计算星期

[算法系列之十八]海量数据处理之BitMap

一:简介 所谓的BitMap就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素.由于采用了bit为单位来存储数据,因此在存储空间方面,可以大大节省. 二:基本思想 我们用一个具体的例子来讲解,假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复).那么我们就可以采用BitMap的方法来达到排序的目的.要表示8个数,我们就只需要8个bit(1Bytes). (1)首先我们开辟1字节(8bit)的空间,将这些空间的所有bit位都置为0,如下图: (2

缓存淘汰算法系列之3——FIFO类

缓存淘汰算法系列之3--FIFO类 1 FIFO 1.1. 原理 按照"先进先出(First In,First Out)"的原理淘汰数据. 1.2. 实现 FIFO队列,具体实现如下: 1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动: 2. 淘汰FIFO队列头部的数据: 1.3. 分析 l 命中率 命中率很低,因为命中率太低,实际应用中基本上不会采用. l 复杂度 简单 l 代价 实现代价很小. 2. Second Chance 2.1. 原理 FIFO算法的改进

算法系列(二十) 计算中国农历(二)

所谓的"天文算法",就是利用经典力学定律推导行星运转轨道,对任意时刻的行星位置进行精确计 算,从而获得某种天文现象发生时的时间,比如日月合朔这一天文现象就是太阳和月亮的地心黄经(视黄 经)差为0的那一瞬间.能够计算任意时刻行星位置的一套理论就被称为星历表,比较著名的星历表有美 国国家航空航天局下属的喷气推进实验室发布的DE系列星历表,还有瑞士天文台在DE406基础上拓展的瑞 士星历表等等.根据行星运行轨道直接计算行星位置通常不是很方便,更何况大多数民用天文计算用不上 那么多精确的轨道参

算法系列(十九) 用天文方法计算日月合朔(新月)

中国农历的朔望月是农历历法的基础,而朔望月又是严格以日月合朔发生的那一天作为月首,因此日 月合朔时间的计算是制定农历历法的关键.本文将介绍ELP-2000/82月球运行理论,以及如何用ELP- 2000/82月球运行理论计算日月合朔时间. 要计算日月合朔时间, 首先要对日月合朔这一天文现象进行数学定义.朔望月是在地球上观察到的月相周期,平均长度约等于 29.53059日,而恒星月(天文月)是月亮绕地球公转一周的时间,长度约27.32166日.月相周期长度比恒 星月长大约两天,这是因为在月球绕地球

算法系列(十四) 狼、羊、菜和农夫过河问题

题目描述:农夫需要把狼.羊.菜和自己运到河对岸去,只有农夫能够划船,而且船比较小,除农 夫之外每次只能运一种东西,还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊. 请考虑一种方法,让农夫能够安全地安排这些东西和他自己过河. 这个题目考察人的快速逻辑运算和短期记忆力.分析一下,在狼->羊->菜这个食物链条中 ,"羊"处在关键位置,解决问题的指导思想就是将"羊"与"狼"和"菜"始终处于隔离状态,也 就是说

数据结构与算法系列(1)时间测试

前言 好久都把数据结构和算法的东西忘完了,最近想重温下这些知识.因此就写了<<数据结构与 算法系列>的文章,希望能和大家共同学习,共同探讨这方面的知识.希望大家多多指异. 1.时间测试 由于本部分内容采用了一种实用的方法来分析数据结构与算法检测,所以在这里避开了使用大O分 析法,而采用运行简单基准测试的方法来代替.这种测试将会说明运行一段代码需要多少秒数(或者 无论什么时间单位). 基准法测试是使用时间测试的方法来衡量运行完整算法所花费的时间长度.如同科学一样,基准测 试也像是一门艺术,

[算法系列之二十四]后缀树(Suffix Tree)

之前有篇文章([算法系列之二十]字典树(Trie))我们详细的介绍了字典树.有了这些基础我们就能更好的理解后缀树了. 一 引言 模式匹配问题 给定一个文本text[0-n-1], 和一个模式串 pattern[0-m-1],写一个函数 search(char pattern[], char text[]), 打印出pattern在text中出现的所有位置(n > m). 这个问题已经有两个经典的算法:KMP算法 ,有限自动机,前者是对模式串pattern做预处理,后者是对待查证文本text做预处

[算法系列之十九]最长公共子序列

题目 最长公共子序列 分析 有两个字符串S1和S2,求一个最长公共子串,即求字符串S3,它们同时是S1和S2的子串,且要求它们的长度最长,并确定这个长度.这个问题我们称之为最长公共子序列问题. 与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用dp[i][j]表示S1中前i个字符和S2中前j个字符分别组成的两个前缀字符串的最长公共子串长度.显然的,当i,j较小时我们可以直接给出答案,如dp[0][j] 必等于0.那么,假设我们已经求的dp[i][j](0 <= i < x,0 &