zoukankan      html  css  js  c++  java
  • zigzag压缩算法

    前文 Base 128 Varints 编码(压缩算法) 介绍了Base 128 Varints这种对数字传输的编码,了解到了这种编码方式是为了最大程度压缩数字的。但是,在前文里,我们只谈论到了正数的情况,那如果出现了负数,该怎么办?zigzag压缩算法解决的就是这个问题。

    在聊这个算法之前,我们得先补补课,聊聊二进制补码相关的东东。

    一、二进制及补码

    我们知道,计算机存储的数据都是二进制的01串,而数字的01串又是以补码的形式存储的,补码是什么东西?为什么要用补码?下面我们一个个来看:

    1.原码

    要了解补码,首先要了解原码。

    什么是原码?简单点说,就是把十进制转为二进制的表示形式,我们用第一个位表示符号(0为非负数,1为负数),剩下的位表示值。比如:

    [+8] = [00001000]原

    [-8] = [10001000]原

    2.反码

    我们用第一位表示符号(0为非负数,1为负数),剩下的位,非负数保持不变,负数按位求反。比如:

    [+8] = [00001000]原 = [0000 1000]反

    [-8] = [10001000]原 = [1111 0111]反

    就是说:正数的反码是本身,负数的反码保持符号位不变,其他位逐位取反

    3.补码

    首先,我们来看为什么会出现补码,我们先来看2个问题

    第一,0居然用2个编码(+0和-0)来表示了:

    原码:[0000 0000]原 = [1000 0000]原

    反码:[0000 0000]反 = [1111 1111]反

    第二,我们来看一个现象

    1 + (-1)

    = [00000001]原 + [1000 0001]原

    = [10000010]原

    = -2

    明显是不对的!

    反码:

    1 + (-1)

    = [00000001]反 + [1111 1110]反

    = [1111 1111]反

    = -0

    表现的好诡异!(正常应该是 0000 0000)

    为了解决这些问题,我们在计算机体系中引入了补码。

    现在我们来看补码怎么取:

    我们用第一位表示符号(0为非负数,1为负数),剩下的位非负数保持不变,负数按位求反末位加一。

    [+8] = [00001000]原 = [0000 1000]补

    [-8] = [10001000]原 = [1111 0111]反 = [1111 1000]补

    即:正数的补码是自己本身,负数的补码口诀为:逐位取反,末位加1

    引进了补码之后,我们看看计算的情况:

    1 + (-1)

    = [00000001]补 + [1111 1111]补

    = [0000 0000]补

    = 0

    很明显,通过这样的方式,计算机进行运算的时候,就不用关心符号这个问题,而只需要按照统一的逢二进一的原则进行运算就可以了。

    好了,补充了这些知识之后,可以进入正题了。

    二、zigzag压缩算法

    前文说到了,在绝大多数情况下,我们使用到的整数,往往是比较小的。然而,我们为了正确,又不得不把很多无用的0进行传输。Base 128 Varints编码很好地解决了上面说的问题,但是,如果传输的数字出现了负数,会出现什么情况呢?

    依然举个很简单的例子,假设我要传输数字-1,会怎样传输呢?

    [-1]=[10000000 00000000 00000000 00000001]原 = [11111111 11111111 11111111 11111110]反 = [11111111 11111111 11111111 11111111]补

    前面的废物0,到这里居然变成了废物1了,怎么样才能进行压缩呢?

    zigzag给出了一个很巧的方法:我们之前讲补码讲过,补码的第一位是符号位,他阻碍了我们对于前导0的压缩,那么,我们就把这个符号位放到补码的最后,其他位整体前移一位

    -1= [11111111_11111111_11111111_11111111]补= [11111111_11111111_11111111_11111111]符号后移

    但是即使这样,也是很难压缩的,因为数字绝对值越小,他所含的前导1越多。于是,这个算法就把负数的所有数据位按位求反,符号位保持不变,得到了这样的整数:

    -1= [11111111_11111111_11111111_11111111]补= [11111111_11111111_11111111_11111111]符号后移=[00000000_00000000_00000000_00000001]符号后移

    我们熟悉的前面的一堆0又出现了,又可以愉快地使用Base 128 Varints算法进行编码了。

    那么,如果是正数的情况,这个算法改怎么处理呢?如果数字是正数,符号位一样放到最后,其他数据全部保持不变就行了,例如:

    [1]

    = (00000000_00000000_00000000_00000001)补

    = (00000000_00000000_00000000_00000010)符号后移

    = (00000000_00000000_00000000_00000010)zigzag

    这样一弄,正数、0、负数都有同样的表示方法了,我们就可以对小整数进行压缩了

  • 相关阅读:
    进度条
    打开文件的功能代码 JFileChooser
    我对JAVA的初认知
    集合之五:Set接口
    集合之四:List接口
    集合之三:泛型
    Maven web项目(简单的表单提交) 搭建(eclipse)
    集合之二:迭代器
    集合之一:集合概述
    java的函数
  • 原文地址:https://www.cnblogs.com/billmiao/p/9872159.html
Copyright © 2011-2022 走看看