ZigZag压缩算法

2,450 阅读3分钟

简介

前面说到varint算法是一种紧凑的压缩数据方法,对于比较小的数值压缩效率很高,但是对于大的数据效率不但没有提升可能还会有所下降。而负数在计算机中的表示就是一个很大的数值。为了达到压缩效果就需要一个方法,把负数转换成小的正数的方法,而zigzag算法正好解决这个需求。

前提

在介绍zigzag之前,要先说说计算机存储中的原码,反码和补码。接下来以十进制5,int32存储类型举例,第一位为符号位,其他位数值来表示。

  • 原码 用符号位和数值表示带符号数,正数的符号位用“0”表示,负数的符号位用“1”表示,数值部分用二进制形式表示。例如:
5的二进制原码
00000000 00000000 00000000 00000101
-5的二进制原码
10000000 00000000 00000000 00000101
  • 反码 正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。
5的二进制反码
00000000 00000000 00000000 00000101
-5的二进制反码
11111111 11111111 11111111 11111010
  • 补码 正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。
5的二进制补码
00000000 00000000 00000000 00000101
-5的二进制补码
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011

再看看-1的二进制存储的表示方法

原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111

可以看出在计算机中存储的负数非常大,在去零压缩中没有任何优势。

ZigZag

ZigZag编码将有符号整数映射为无符号整数,以便具有较小绝对值(例如-1)的数字也具有较小的编码值。这样做的方式是通过正整数和负整数来回“曲折”,以便将-1编码为1,将1编码为2,将-2编码为3,依此类推如下图所示。

int32位实现代码:

func ZigZag32(n int) int {
	return (n << 1) ^ (n >> 31)
}

int64位实现代码:

func ZigZag64(n int) int {
	return (n << 1) ^ (n >> 63)
}

第二个移位((n >> 31)或(n >> 63)部分)是算术移位。因此,换句话说,移位的结果是全为零的数字(如果n为正)或全为一个的数字(如果n为负)

总结

通过zigzag编码把负数变成小整数,然后更利于做其他的去零压缩操作,如果遇见大的负数,然后再去零压缩则优势完全丧失。