《数据结构》哈希,位图,布隆过滤器之大数据

一.哈希切割top K问题

   1.   给一个超过100G大小的log file,log中存放着IP地址,设计算法找到出现次数最多的IP地址。
      在进行 大数据处理时惯用的思路是:是否有特殊的数据结构可以解决,如果不允许使用特殊数据结构那么可以考虑进行切割划分。如果直接按照惯例进行均分,就会很大概率出现一些问题,这个时候就可以采用哈希切割啦。
      使用哈希切割法进行除留余数法,取模后相同的值会进入划分后编号相同的文件(划分X份文件,则切割时模X,余数从0到X-1,这是我们的文件编号从file0到fileX-1),这样就会使得相同的IP进入同一个文件。
      题中所讲这里有超过100G的log file,那么可以切1000份,切割后每个文件只有100M大小已经可以供我们进行使用。
      HashFunc(IP)%1000.


位图应用

2.给定100亿个整数,设计算法找到只出现一次的整数

日常生活中10亿个字节占1G内存,那么10亿个整数占4G内存,100亿个整数就是40G。

那么问题来了,一般电脑工作内存为4G,好点的是8G,这些数据的内存大于计算机所能存储的内存所以无法存储下来。这时候该怎么办呢,内存无法存下那我们该怎么读取,也许你会想到直接从硬盘中读取,可以,但是硬盘读取速度很慢需要很多时间。这个时候我们有更好的办法,就是使用位图位图就是给定一段连续的地址空间然后让这个空间每一个位都为0,再然后让每一个位表示一个数字,当这个数字出现就将对应的位置为1。

题中所给这里有100亿个数字,就是100亿个位,也就是12.5亿个字节,也就是1.25G左右,这时候用来存储数据绰绰有余。

找到只出现1次的整数我们可以用俩个位映射一个值,具体实现如


 
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
#include<string.h>


typedef struct TwoBitSet{
	size_t *_bits;
	size_t _range;
}TwoBitSet;


void TBSinit(TwoBitSet* tbs,size_t range);
int TBSGetState(TwoBitSet* tbs,size_t x);
int TBSSetValue(TwoBitSet* tbs,size_t x,int value);
void TBSDestory(TwoBitSet* tbs,size_t range);


void TBSinit(TwoBitSet* tbs,size_t range)
{
	assert(tbs);
	tbs->_bits = (size_t*)malloc(((range>>4)+1)*sizeof(size_t));//用俩个位映射一个值,则每块只需要16个字节的空间
	assert(tbs->_bits);
	tbs->_range = range;
	memset(*(tbs->_bits),0,(range>>4)+1)*sizeof(size_t));
}


int TBSGetState(TwoBitSet* tbs,size_t x)
{
	assert(tbs);
	int index = x>>4;
	int num = x%16;
	num *= 2;


	int value = tbs->_bits[index] >> num;
	
	return value & 3;
}


int TBSSetValue(TwoBitSet* tbs,size_t x,int value)
{
	assert(tbs);
	int index = x>>4;
	int num = x%16;
	num *= 2;


	if(value == 0) //用俩位映射一个数,把X置俩位为00
	{
		tbs->_bits[index] &= ~(3<<num);
	}
	else if(value == 1)  //01,因为有俩个位,
		//所以有时候可能会不清楚最后计算出的num是这个数的低位还是高位,
		//通过举例我们知道num是这个数的低位
		//那么num+1就是这个数的高位
	{
		tbs->_bits[index] |= (1<<num);  //先把地位置为1
		tbs->_bits[index] &= ~(1<<(num+1));  //把高位置为0
	}
	else if(value == 2)  //10
	{
		tbs->_bits[index] &= ~(1<<num);
		tbs->_bits[index] |= (1<<(num+1));
	}
	else if(value == 3)   //11
	{
		tbs->_bits[index] |= (3<<num);
	}
	else
	{
		assert(false);
	}
}


void TBSDestory(TwoBitSet* tbs,size_t range)
{
	assert(tbs);
	tbs->_bits = NULL;
	tbs->_range = 0;
}


int main()
{  //伪代码
	while(ReadFileEnd())
	{
		x = ReadNumFromFile();
		if(value < 2)
			TBSSetValue(&tbs,value+1)
	}


	for(size_t i = 0;i < size-1;++i)
	{
		if(TBSGetValue(&tbs,i) == 1)
			printf(i);
	}
	return 0;
}



3.给俩个文件,分别有100亿个整数,我们只有1G内存,如何找到俩个文件交集
整数使用位图,与下面第5题有异曲同工之妙。


4.1个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的整数
不难看出,这个题和第二个题是一个题型,我们对第二个题的伪代码进行适当调整就可以解决此题,在这里就不再继续一一详细说明。


布隆过滤器+哈希切分

5.给俩个文件,分别有100亿个query(查询),我们只有1G内存,如何找到俩个文件交集?分别给出精确算法和近似算法

在这里要记得,整数用位图,其他的用布隆过滤器。直接上图呗...



6.如何扩展BloomFilter使得它支持删除元素的操作
           大家都知道都知道, 原始的布隆过滤器是不支持删除的,因为存在一种情况:俩个不相等的数,它们映射到其中的一个(有时候也可能是几个)位会重合在一起,这时候如果要删除其中一个数,就要将它所有的位都由1置为0,
   置0之后你就会发现:在查找和它有重合为的数就会发生查找失败。
           那么  这时你可能会想,不会呀,我在删之前可以先判断一下,如果发现一个位被其他数字也映射到了,那我就不删它不就就OK啦。真的这样么?可能么?你怎么能知道一个为1的位它被多少个数字映射了呢?
           不知道你有没有抓住上一句话的重点呢,上句话提到“被多少个数字映射”,这时候就可以用一种方法,叫做“ 引用计数”。
            在引用计数中,每一个对象负责维护对象所有引用的计数zhi。当一个新的引用指向对象时,引用计数器都递增;当去掉一个引用时,引用计数器就递减,当引用计数到零时,该对象就将释放占有的资源。
           通过这个方法,我们可以将位每次被映射的时候都计数+1,删除的时候-1,当减到0时就说明已经没有数字映射到这个位啦。
           
7.如何扩展BloomFilter使得它支持计数的操作
       有没有感觉这个题很熟悉?好像刚才见过。


倒排索引

8.给上千个文件,每个文件大小为1K-100M.给n个设计算法对每个词找到所有包含它的文件,你只有100 K内存。
    有没有感觉这个题问的好熟悉,我们每天使用的浏览器不就是这样子的么?比如说经常使用的百度浏览器,输入想要知道的内容,搜索后出来一个一个包含你想知道内容的搜索引擎供用户选择,点开引擎后会进入一个界面,其实这个界面本质上就是一个文件,文件里是Html脚本(把界面保存成为文件后,打开文件,你会发现文件中的内容与你浏览器看到的界面完全不同,文件中的是好多复杂的代码,这些代码是html...),那为啥浏览器界面却会做的那么精致易懂呢?那是因为浏览器本身就是一个脚本执行器,它只执行HTML。如果你接触过前端那这个原理你肯定是懂得(如果我哪里解释的有问题,前端大神看到一定不要边敲电脑边mmp哦,因为我对前端只是看到它的大门而已还没入门呢,写的时候心里总有点慌慌的感觉哪里不对。如果有问题欢迎前来评论哦,我一定马上吸取修改)。怎么样,是不是感觉我们的浏览器在你心目中更加高大上了呢
    根据名字, 倒排索引相应的也会有正向索引,正向索引对应的也会有反向索引,是不是感觉这个名字怪怪的,反向索引还有另外一个名字:倒排索引。是不是感觉这个名字更加高大上更加觉得耳熟呢,哈哈,开玩笑的。
    倒排索引中的 倒排很容易误认为就是A-Z成为Z-A的倒排,当然不是这个意思了,到底是什么意思,我们下面会对此进行解释。
    讲解倒排索引之前我们先来说下 正向索引,为啥有正向和倒排索引而我们却更倾向于倒排索引呢,这是因为倒排索引相对正向索引更具有优势。
         在搜索引擎里 每个文件都有一个对应的文件ID,比如“file1,file2....”文件内容被表示为一系列 关键词组成的集合。flie1经过分词,提取出10个关键词, 每个关键词都会记录它出现的次数以及位置

         正向索引是把文件ID对应到关键词的映射,即通过找到含有关键次的文件然后通过一系列的排序呈现给用户,是一种通过key找value的模式。

       正向索引模式图如下


                 例如在淘宝网中搜索“面膜”,假设只能正向索引,那么就需要扫描所有索引库中的文件,找到含有关键词的文件,然后通过排序(价格从高到低,销量优先....)给用户呈现出来。
但是索引库中的文件数目是一个非常庞大的数字,扫描它们会花费大量的时间,根本就无法实现用户想要实时想要拿到返回结果的需求。因此正向索引是不可取的。
           倒排索引那既然正向索引不可取,那我们就想到了倒排索引。

                 之前正想索引是文件ID到关键词的映射,那我们就用关键词到文件ID的映射,即倒排索引。从词的关键字找到文件。 

        倒排索引模式图如下


下面是倒排索引一个小小的栗子,用来帮助更一步理解倒排索引



谢谢观看。

    
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值