从一道面试题说去

    有一道面试题: 给定n个整型数,怎样让这n个数的使用空间最小。

     ok,我们都知道在32位的机器下,int类型的数占4个字节,因此n个数总的使用空间应该是4n。(64位不做解释)那我们怎么样才能使得n个数字的使用空间最小呢?

    一. 我们先来看一个例子

          假设现在有3个数,1,2,3。

          我们都知道数字最后都是以二进制的方式存储的,我们可以表示出1,2,3的二进制

          1: 0000 0000 0000 0000 0000 0000 0000 0001

          2: 0000 0000 0000 0000 0000 0000 0000 0010

          3: 0000 0000 0000 0000 0000 0000 0000 0011

          正常情况下,要表示1,2,3这三个数,我们必须使用32*3 = 96 bit。我们发现其实这样是非常浪费的,因为对于这三个数来说实际上用到了只是最开始的8个bit,而前面的24个bit都是没有用的。

    二. 我们要怎么压缩,才能使得空间变小呢?

          如果听说过google protobuf的同学就应该马上能知道,压缩的方式了。没错,方法正是protobuf使用的variant的编码方式。

          1. 首先介绍一下什么是protobuf?protobuf是google提出的一种非常强大的结构化数据存储方式,可以通过序列化和反序列化来进行数据操作,详情请走google

          2. 介绍一下protobuf的variant的编码方式

             普通的一个int二进制是32位,4个字节。而protobuf的一个int并不是这么表示,protobuf会把每个字节的8个bit中最高位当做标记位,标记下一个字节是否属于当前这个数。

             比如1,普通的二进制表示是0...0 0000 0001,会占用32位。而protobuf 只要表示成0000 0001,最高位0表示下一个字节不属于当前这个数,因此1只要占用1个字节的空间即可。

          3. variant是怎么进行压缩的呢?

             假设protobuf要存储一个数300(我们只是单纯考虑压缩,不考虑其它存储的方式因为里面涉及到了key-value键值对,感兴趣的同学可以具体去看看google的文档)。

             a.300的二进制如下: 0000 0000 0000 0000 0000 0001 0010 1100

             b. 通过上面我们知道,protobuf的每个字节有一个bit用来标记,有7个bit用来表示值。因为我们对300的二进制从后往前每7个bit进行分割,0101100,0000010,0000000,0000000,0000。去掉后面三组都是0,现在只剩下两组0101100,0000010。 

                 因此一个300的varint表示成1010110000000010。只需要占用2个字节,比原来少了2个字节,因此可以节省空间。

             c.可能有的同学就会问了,既然variant每个字节只能表示7个bit,那就说4个字节最多只有28个bit能表示值,那么当一个数大于等于2^28的时候不是要用5个字节表示?那当n个数都大于2的28次方的时候,不是比4n的空间还大吗?

                没错,这确实是protobuf的一个问题,但是后面我们会有办法来优化这个问题。

         4. protobuf的解压缩方式

            a. 我们了解了variant的压缩方式之后,应该就能明白是怎么解压缩的了。假设我们拿到了一段variant二进制编码101011000000001010000001....,我们从头开始读,当发现当前自己的最高位为0的是就是说明这个数结束了。

            b. 比如我们读到了1010110000000010

                第一步,去标记位: 0101100 0000010

                第二步,交换数据: 0000010 0101100 这一步看不懂的同学请仔细看一些上面的压缩方式。

                第三步,计算: 0000010 0101100 =  2^2 + 2^3 + 2^5 + 2^8 = 300

      

      三. 怎么样做,才能使得空间最小呢?

           别忘了,我们还有一个问题没有解决。就是关于当数值大于2^28次方的时候,使用variant编码方式会使得空间大于4*n。

           ok,假设有3个数2147483645,2147483646,2147483647。正常情况下存储这三个数需要12个字节,如果使用variant每个数需要5个字节,则要15个字节,发现此时并不能减少空间,反而使空间增大。

           在压缩算法里面有一个很经典就是 增量存储。比如这个三个数,我们利用增量存储则可以表示成2147483645,1,1。然后再利用variant存储,则总共只需要5+1+1 = 7个字节,发现此时我们就可以把空间从15压缩到了7,发现收益还是非常可观。

           增量存储是一个非常重要的思想,好比搜索引擎的索引。对于一个商业的索引引擎来说,索引可能是有几百亿,对海量的索引进行压缩存储,收益是非常惊人的。

        

     

时间: 2024-11-03 04:03:09

从一道面试题说去的相关文章

从一道面试题说去4

题目:假设公司有30w人,每个人编号从1~30w.现在公司举办年会,要求随机10w个人出来做为中奖的员工. 分析:30w人随机10w人,利用rand函数即可,但是考虑到随机数有可能重复,加个set去重即可. 代码: #include <stdio.h> #include <stdlib.h> #include <time.h> #include <iostream> #include <algorithm> #include <set>

从一道面试题说去 2

面试题目: 给定一个数n,求1*2*3*...*n 结果中末尾0的个数. 1. 我们先看一个特殊的例子,假设n是100的情况下.     根据题目的意思,我们需要求的是1*2*3*...*100的结果中末尾0的个数.     回顾一下小学4年级的数学知识,一个数末尾加一个0,相当于乘上一个10.     ok,假设有一个能够被10整除的数sum,那么sum = 10*N => 2*5*N.例如 50 = 10*5 = 2*5*5     好了,通过上面,我们可以发现,10的个数是由2和5的个数,

《Wireshark网络分析就这么简单》—从一道面试题开始说起

从一道面试题开始说起Wireshark网络分析就这么简单从一道面试题开始说起我每次当面试官,都要伪装成无所不知的大牛. 这当然是无奈的选择--现在每封简历都那么耀眼,不装一下简直镇不住场面.比如尚未毕业的本科生,早就拿下CCIE认证:留欧两年的海归,已然精通英.法.德三门外语:最厉害的一位应聘者,研究生阶段就在国际上首次提出了计算机和生物学的跨界理论--可怜我这个老实人在一开场还能装装,到了技术环节就忍不住提问基础知识,一下子把气氛从学术殿堂拉到建筑工地.不过就是这些最基础的问题,却常常把简历精

《大咖讲Wireshark网络分析》—从一道面试题开始说起

从一道面试题开始说起大咖讲Wireshark网络分析我每次当面试官,都要伪装成无所不知的大牛. 这当然是无奈的选择--现在每封简历都那么耀眼,不装一下简直镇不住场面.比如尚未毕业的本科生,早就拿下CCIE认证:留欧两年的海归,已然精通英.法.德三门外语:最厉害的一位应聘者,研究生阶段就在国际上首次提出了计算机和生物学的跨界理论--可怜我这个老实人在一开场还能装装,到了技术环节就忍不住提问基础知识,一下子把气氛从学术殿堂拉到建筑工地.不过就是这些最基础的问题,却常常把简历精英们难住.本文要介绍的便

一道面试题(关于千万量级数据结构排序)

问题描述 一道面试题(关于千万量级数据结构排序) 题目: 已知文件中存有全国英语六级历年来的成绩(千万级别,考生分数都是正整数,最高710分),每一行都是一个人的姓名.考号和成绩,请你对考生的成绩从高到低进行排序,输出到另一个文件中. 格式 如下: 李四,201008823,678: 张三,201007432,356: 王五,201322233,464: 排序后: 李四,201008823,678: 王五,201322233,464: 王五,201322233,464: 要求:使空间复杂度和时间

语言 面试题-一道面试题,不是很清楚这个例子怎么解答,求大神帮助.

问题描述 一道面试题,不是很清楚这个例子怎么解答,求大神帮助. 提问是 这段代码有什么问题, 有什么解决思路.(我其实连问题都没看出来,代码可以编译) // Memory-mapped peripheral#define STATUS_REG_ADDR 0x12345678 // 32-bit status register#define DATA_REG_ADDR 0x1234567C // 32-bit data register // Status register bits#define

初始化顺序-今年阿里巴巴的一道笔试题

问题描述 今年阿里巴巴的一道笔试题 public class Test1 { public static int k = 0; public static Test1 t1 = new Test1("t1"); public static Test1 t2 = new Test1("t2"); public static int i = print("i"); public static int n = 99; public int j = pr

结构体定义-如何定义满足以下的Node与List结构体,今天参加斐讯的一道笔试题。

问题描述 如何定义满足以下的Node与List结构体,今天参加斐讯的一道笔试题. Node包含50个字符.

一道面试题:布尔变量

FROM:酷壳 下面这篇文章是从StackOverflow来的.LZ面试的时候遇到了一道面试题:"如果有三个Bool型变量,请写出一程序得知其中有2个以上变量的值是true",于是LZ做了下面的这样的程序: boolean atLeastTwo(boolean a, boolean b, boolean c) { if ((a && b) || (b && c) || (a && c)) { return true; } else { r