概述
前缀编码,有时也被称为前向编码,是另一个通过移除冗余数据来降低数据量的算法。思想非常简单,但算法实现比较困难。要了解原因,首先我们来看一看它的原理。
请看下面的字典。
1 2 3 4 5 6 7 8 |
use used useful usefully usefulness useless uselessly uselessness |
为了不使用纯文本保存这些单词或者在网络上传输,我们可以用前缀编码进行压缩(编码)。
很明显,每一个单词都以表中的第一个单词“use”为前缀。所以我们很容易将它们压缩成下面的数组。
1 2 3 4 5 6 7 8 9 |
$data = array( 0 => 'use', 1 => '0d', 2 => '0ful', 3 => '0fully', 4 => '0less', 5 => '0lessly', 6 => '0lessness', ); |
显然这并不是最佳的压缩结果,在不仅仅使用第一个词作为前缀的情况下,我们可以更进一步压缩。
1 2 3 4 5 6 7 8 9 |
$data = array( 0 => 'use', 1 => '0d', 2 => '0ful', 3 => '2ly', 4 => '0less', 5 => '4ly', 6 => '4ness', ); |
此时的压缩更好,好消息是解码是一个相对简单的过程。但棘手的部分在于压缩本身。问题是选择合适的前缀非常困难。第一个例程的前缀选择很简单,但事实上,大多时候数据很混乱。的确,对于随机产生的数据压缩过程将非常困难,算法过程不仅很慢,而且难以实现。
好的方面是,如果我们事先知道数据的格式,该算法可以用于多种情况。那么,让我们看看下面三个例子,该算法可能会很方便。
应用
以下是三个前缀编码的例子。前面我说随机数据的压缩过程会很难,如果你事先知道输入数据的格式,这将会是一个很好的练习。
日期和时间前缀
我们通常会忽略年份的前两个数字,例如我们通常不会写1995或1996,而是使用更短的——‘95’和‘96’。这样年份就被编码成更短的字符串。
1 2 |
input: (1991, 1992, 1993, 1994, 1995, 1996) output: (91, 92, 93, 94, 95, 96) |
问题在于输入流发生很小的变动,解码就会出错。如果我们加上21世纪的年份,我们将失去数据的唯一性。
1 2 |
input: (1998, 1992, 1999, 2011, 2012) output: (98, 92, 99, 11, 12) |
此时,解码器肯会将最后两个数值解码成(1911, 1912),因为“19”被认为是前缀。所以,我们事先必须知道前缀与每一个数值绝对平等。如果没有编码格式,必须不同。例如我们可以使用一些特殊标识和前缀一起编码。
1 2 |
input: (1998, 1992, 1932, 1924, 2001, 2012) output: (#19, 98, 92, 32, 24, #20, 01, 12) |
一旦解码器读到#字符,它就知道下面的数为前缀。
事实上,这种方法可用于日期和时间格式的编码。假设我们有一些日期时间值,而且我们知道所有数据都是在同一天。
1 2 3 4 |
2012-01-31 15:33:45 2012-01-31 16:12:11 2012-01-31 17:32:35 2012-01-31 18:54:34 |
显然,我们可以忽略这些字符串的时间部分,仅发送(保存)时间。当然,我们必须确定所有的这些数值都是在同一天。如果不是,我们可以使用上例中的方法。
电话号码
电话号码是前缀编码的典型应用。不仅仅是国际代码,移动网络运营商的电话号码也使用前缀编码。如果我们要传输电话号码,假设是英国的,我们可以用一些更短的东西替换开头的“+44”。
如果你要给移动设备编写电话簿,你可以通过前缀编码压缩数据,节省部分空间,这样用户将拥有更多空间,也可以在手机上存储更多电话号码。
电话号码前缀也适用于数据库标准化。这样你可以将它们存储在单独的数据库表中,不用电话簿中唯一的号码。
地理坐标
对于我之前帖子中使用的例子,可以在一定范围内去掉通用前缀来发送地理坐标。的确,在必须传送大量坐标到地图应用时,你可以预期这些标记在一定范围内彼此间相当靠近。
在一定范围内,可以预期这些标记都有相同的前缀。
那些点的坐标有共同的前缀,就像下面地铁站的例子一样。
1 2 3 4 |
LatLon(40.762959,-73.985989) LatLon(40.761886,-73.983629) LatLon(40.762861,-73.981612) LatLon(40.764616,-73.98056) |
我们可以发现所有的地理坐标点有相同的前缀(40.76x, -73.98x),所以我们只需要发送一次前缀。
1 2 3 4 5 6 |
Prefix: (40.76, -73.98) Data: LatLon(2959,5989) LatLon(1886,3629) LatLon(2861,1612) LatLon(4616,056) |
以上是前缀编码的三个例子,该算法在传输均匀数据是非常有用。
后缀编码
后缀编码和前缀编码几乎相同,区别在于编码重复后缀。如下例,后缀编码替换最后重复的后缀,这非常有用。
1 2 3 |
Johnson Clarkson Jackson |
或者公司名称。
1 2 3 |
Apple Inc. Google Inc. Yahoo! Inc. |
这里我们可以使用一些其他更短的东西来替换“Inc”。