最近被调过去做日志分析系统,接触所谓的大数据。学校的时候也做过空间数据库索引与数据挖掘研究(LBS方向),但是工作3年一直做通信协议,有些东西也忘掉了。面对茫茫前途,讨厌这种模糊不清的状态,因此,计划系统学习理论并用于实践。后续会写一点相关的技术学习记录。
本周写一下基本的亚线性算法,这里要感谢哈工大的王宏志老师的讲义。
大数据这个字眼在当今生活中随处可见,比如:某某基于大数据的社交网络分析、基于大数据的医疗分析、基于大数据的政企数据分析、基于大数据的金融分析等等。
那什么是大数据?网上给出的定义有很多请自行谷歌或百度。这里说一下我看到的一种定义:
“大数据”就是一种需要新的处理方式才能具有更强决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
从定义上,我们可以获取的信息有:新的处理方式、大数据的作用(决策、发现、流程优化)、以及它的特点(海量 、高增长率、多样化)。哈哈,注意对我来说有个更关键的词资产,我的潜意识“资产==钱”,那它就有研究价值了。
上面定义说我们需要新的处理方式,为什么呢?因为大数据 具有4V特点(很多文章定义为3V):数据量(Volume)、速度(Velocity)、多样性(Variety)、价值(Value)。有了这些特性就给我们处理 大数据 带来如下困难:
- 无法在有限时间内读取所有数据。有人会说现在到处大数据平台hadoop读写数据怎么会说无法读取所有数据呢?我理解是要注意有限时间内!用户在电商网站买个充气娃娃,如果处理慢了,等用户离开了网站,再提供推荐结果已经没有任何意义了。
- 数据难以存放在内存上。数据量很大,内存是有限而且相对于磁盘更贵。因此更多数据是存放在磁盘上。能够用于计算的数据必须是在内存上。
- 单一的计算机已经没办法存储和计算整体数据。其实这个问题算是1、2的结合了。这提示我们扩大硬件数目。
- 计算机的计算能力和知识不足。这个指光靠计算机不行了要靠人的知识参与,人工智能要在大数据领域发挥巨大作用了。
所以,我们要采用新的处理方式了。问题1,有限时间内我们可以牺牲精度采用近似算法读取部分数据(比如抽样算法)来计算结果,这也引出来我们接下来要介绍的时间亚线性算法。问题2,有限的内存空间,根据问题1的讲述,我们肯定会想到采用近似算法读取少量数据仅内存,空间亚线性算法也出来了;同时,既然数据要存到外存那么相关的外存算法。问题3,单一不行我们自然想到了并行计算,并行计算目前使用广泛的计算框架有hadoop、spark等(后续会有系列文档来聊聊我的学习经历和目前做的日志分析系统)。问题4,暂且不提了,后续再聊。
大数据算法设计与分析,顾名思义两方面:设计与分析。算法有但不限于:精确算法设计(我们学校上的算法课,后续也要好好整理整理)、近似算法、并行算法、外存算法、随机算法、在线/数据流算法、面向新体系结构算法(硬件比如GPU、FPGA<业内已经很多家涉入其中比如微软、华为、中兴等>)、现代优化算法。分析:时间空间复杂性(有算法就有它)、IO复杂性(并行计算通信复杂度、外存算法IO复杂度)、结果质量(既然采用了近似计算、随机算法那么就存在结果质量的问题,关键看误差在不在你容忍的范围内)。
扯了半天废话,估计大家也烦了,下面进入正题:亚线性算法。
1. 亚线性算法定义
时间/空间/通讯/IO等消耗复杂度为o(输入规模n)的算法。主要有两种:空间亚线性算法、时间亚线性算法。
1. 空间亚线性算法---水库抽样算法
空间亚线性算法顾名思义就是想我们在小于线性O(n)的空间复杂度完成目标,内存有限是其特征。
这里我以水库抽样算法为例聊聊一下有限空间约束下,我们如何完成设下的目标。
问题描述:
输入:一组源源不断的数据流,数据大小未知
输出:这个数据流的K个均匀抽样
要求:每个数据仅能读取一遍;可用空间为K;当数据累计个数为n时(n>K),保存当前已扫描数据的K个均匀抽样。
首先明确什么叫均匀抽样,大家肯定都知道就是每个数据被等概率抽取呗。
那我们就分开想想,如果就只有n=K个元素,可用空间正好也是K,那我们正好把K个数据都保存起来,均匀吧!每个扫描到的数据都存起来了!
但是如果n>K呢?要想均匀抽样,概率当然是
如何做到呢?两种往k/n上靠的思路,我当时想到的是方法一(PS:这道题也是3年前博主校招的题目)包括两点:
- a. 每个新到达的元素 i 被选人有限空间(K数组)的概率是K/i;
- b. 第n个元素过来的时候我们之前选取的元素还在有限空间内的话,那么它的概率应该是K/n就可以了。
如果能验证证明就可以了,因此,方法一如下:
空间总共K个,第i个元素过来的时候,它以K/i的概率被抽取,第n过来的时候它被抽取到的概率是k/n。即求第n个元素的过来的时候它留在K个空间的概率:
这样就证明了。下面代码就好写了:
void getRandomSamplesK(int *pdataStream, int n, int k)
{
int i = 0;
int *randomSampleK = (int*)malloc(k*sizeof(int));
int r = 0;
int replaceIndex = 0;
int j = 0;
if(NULL == randomSampleK)
{
return;
}
for(;i<k;i++)
{
randomSampleK[i]=pdataStream[i];
}
for(;i<n;i++)
{
srand((unsigned int)time(NULL));
r = (int)rand();
replaceIndex = r%i;
if(replaceIndex<k)//被选中替换的概率(k/i+1)*(1/k)
{
randomSampleK[replaceIndex] = pdataStream[i];
}
}
for(;j<k;j++)
{
printf("%d ",randomSampleK[j]);
}
free(randomSampleK);
return ;
}
方法二,利用数学归纳法证明:
前提:(1)前K个元素被等概率选中的概率为1;(2)已扫描的n个元素被等概率抽样的概率为k/n;即第n个新来的元素被选中的概率为k/n。
假设:第i(i>k)个元素被抽中的概率为k/i;原蓄水池中的元素仍旧在蓄水池中的概率为k/i;
证明目标:第i+1个元素被抽中的概率为k/(i+1);原蓄水池中的元素仍旧在的概率为k/(i+1);
证明:
(1)第i+1个元素被抽中的概率为k/(i+1);
(2)根据假设第i个元素到来,原蓄水池元素概率为k/i;事件A={第i+1个元素被抽中后,原元素仍旧在蓄水池}的对立事件为B={第i+1个元素被抽中后,原元素被替换}。因此:
P(A) = 1-P(B);
因为事件C={扫描了i+1个元素,原蓄水池中元素仍在}是串联:原元素在蓄水池,第i+1没有替换原元素;
所以:
本小节总结:本节通过一个面试经常遇到题目讲述了空间亚线性算法通过有限的内存空间来获得了一个数据流的抽样信息,这里注意虽然我们做了抽样但是却没有近似,不是所有抽样算法都是近似算法。学习算法一方面是为了锻炼自己的逻辑思维;更重要的是要学以致用,要知道算法的应用场景!蓄水池抽样算法最直观的应用就获取随机数(不是库函数提供的伪随机数),后续会整理讲述的算法的应用场景,这里mark一下!
2. 空间亚线性算法---数据流中最频繁元素
上一小节分享的蓄水池抽样算法虽然是抽样算法却不是近似算法。本节分享一下有限内存利用近似解求数据流中最频繁的元素。
先讲解一下大数据的数据流模型特点:
- 数据只能扫描1次或几次;
- 能够使用的内存是有限的(内存<<数据规模);
- 希望通过维护一个内存结果(数据流概要)来给出相关性质的一个评估;
总结来说:数据要快速处理;空间亚线性。
问题描述:
输入:大数据流序列<x1,x2,x3... ...>。
输出:找出数据流中当前扫描的元素序列中出现最频繁的元素的信息。
问题分析:当前已扫描元素数目m,元素种类n,内存可用单元数k,n>>k。
元素种类为n,如果分配n个计数器统计个数,最后比较得到值最大的那个元素就是最频繁的元素,计数器的值就是其出现的次数。但是这里要求n>>k。怎么办呢?
算法: 当一个元素x1到来,
- IF x1 所属类的计数器在内存中不存在且内存中有空闲的计数器,THEN 给x1分配一个计数器,计数器值+1;
- IF x1所属类的计数器在内存存在,THEN x1对应的计数器+1;
- IF x1所属类的计数器在内存不存在且内存中无空余的计数器,THEN k个计数器的值均-1;当计数器的值等于0时,此计数器为空闲计数器。
实例化(按照下图大家应该可以能够自己举个例子理解一下):
32, 12, 14, 32, 7, 12, 32, 7, 6, 12, 4,
大家实例化后肯定会说这个结果不准。
比如:32, 12, 14, 32, 7, 12, 6, 7, 8, 4(m=10,k=3,n=7)
32:+1,+1,-1,-1---空闲---8:+1,
12;+1,-1,--空闲---12:+1,-1---空闲---4:+1
14:+1,-1,---空闲---6:+1,-1---空闲---
最频繁的元素是8、 4,这显然是有误差的。上述算法在什么场景下可以找出最频繁的元素呢?答案是最频繁的元素的个数超过m/(k+1);这样,最频繁元素才能抵消其他种类元素而存在于内存中。因此这里有个很重要的原则:
Zipf原则:典型的概率分布是高度倾斜的,只有少数的最频繁元素。占元素种类10%的元素却占了元素总个数的90%。
接下来,如果我们需要知道最频繁元素出现的次数的话,内存中的概要数据误差是多少?即该元素的计数器被减去了多少?
分析:每次计数器减少会一次性减少k个,另外本次输入的元素也不再计入次数,因此一次减少了k+1个;总共减少了m-a(k个计数器的和);所以,每个计数器减少了(m-a)/(k+1)个。
结论:
- 误差与内存大小K成反比,说明内存越大误差越小;
- 当扫描的元素总数m>>(m-a)/(k+1),可以得到一个好的概要估计
- 该算法成立前提:Zipf原则
算法算是说明白了吧?如果有问题请大家随时邮件(qiaowei0077@126.com)交流。下面附上找频繁元素的小代码,此问题也可以应用于一道面试题(寻找发帖水王,条件发的帖子超过m/k!)。
/*
*elem:数据流中的元素:type:类型,value:值
*counterArray:计数器数组:counter:技术,type:元素类型
*counterNum:计数器数目
*/
void findFrequent(Element elem,Counter *counterArray,int counterNum)
{
/*如果该类型存在,则返回计数器ID;
*如果该类型不存在且计数器有空余,则返回新分配的计数器ID;
*如果该类型不存在且计数器无空余则返回-1;
*/
int counterID = getCounter(elem.type,counterArray,counterNum);
if(-1==counterID)
{
for(int i = 0;i<counterNum;i++)
{
counterArray[i].counter--;
}
return;
}
counterArray[counterID].counter++;
counterArray[counterID].type = elem.type;
return;
}
第一次写技术博客,有问题请大家指出来,qiaowei0077@126.com!