大数据算法之——DGIM算法

    最近学习大数据,遇到了DGIM算法,其题目如下:

    In Amazon, for every product X we keep 0/1 stream of whether that product was sold inthe n-th transaction. We want to answer queries, how many times have we sold X in the lastK sales. Supposed that we have ten-thousand records of product X in Amazon. You are required to program tocomplete the query in last one thousand sales through DGIM algorithm. Output shoulddemonstrate how you set the buckets and your estimate.Updating rules:

        (1) If the current bit is 0, no other changes are needed;

        (2) If the current bit is 1:

            (a) Create a new bucket of size 1, for just this bit, and end timestamp = current time;

            (b) If there are now three buckets of size 1, combine the oldest two into a bucket of size2;

            (c) If there are now three buckets of size 2, combine the oldest two into a bucket of size4;

            (d) And analogize for the rest.

  对于该查询,若空间足够,可以通过空间复杂度O(N),时间复杂度O(1)的简单的加减得到查询结果。所以这里要讨论的是对于N非常大的情况,即假设由于存储空间有限,我们不能存储整个窗口的数据。

  根据滑动窗口共有2N种表示简单推算,任何能给出确切结果的算法都需要O(N)的空间,即该问题必须寻找近似算法。Datar-Gionis-Indyk-Motwani Algorithm(DGIM算法)是其中一种:该算法仅使用O(log2N)的空间,时间复杂度为O(logN)。     

  首先假设对于二进制流,其中每个位可以用一个时间戳(timestamp)标志该位进入流的时间(对于大小为N的滑动窗口,该时间戳可以用O(logN)位表示)。

  DGIM算法利用桶(bucket)对滑动窗口进行划分,每个桶保存以下信息:

  • 桶的最右边位即最后进入流的位的时间戳;
  • 桶的大小,即桶中1的个数,该数目位2的幂。

  对于以上信息,保存时间戳需要logN的空间,保存桶的大小需要loglogN的空间。

  使用桶对滑动窗口进行划分时遵循以下5条规则:

  1. 桶最右边的位必须是1;
  2. 1个位最多属于1个桶;
  3. 每种大小的桶有1个或者两个(从1到最大的桶的大小);
  4. 每个桶的大小是2的幂;
  5. 桶的大小从右到左非降序排列;

  DGIM算法中数据结构的更新

  1. 每一个新的位进入滑动窗口后,最左边一个位从窗口中移出(同时从桶中移出);如果最左边的桶的时间戳是当前时间戳减去N(也就是说桶里已经没有处于窗口中的位),则放弃这个桶;
  2. 对于新加入的位,如果其为0,则无操作;否则建立一个包含新加入位的大小为1的桶;
  3. 由于新增一个大小为1的桶而出现3个桶大小为1,则合并最左边的两个桶为一个大小为2的桶;合并之后可能出现3个大小为2的桶,则合并最左边两个大小为2的桶得到一个大小为4的桶……依次类推直到到达最左边的桶。

举例(上图中最右边进入一个新的1后的结果):


可以给出DGIM的存储开销:桶的个数O(logN),每个桶可以用O(logN)空间表示,保存表示一个窗口的所有桶所占的空间为O(log2N)。

  对于查询,首先寻找含最近k个位的拥有最大时间戳的桶b,查询结果为在k个为中所有桶的大小,加上b的大小的一半。一次查询的时间复杂度为O(logN)。

  该估计值误差(fractional error)的上下限为:

  • 至少是正确值的50%:最差情况b中都是1,估计误差值是b的一半。
  • 最多超过正确值的50%:最差情况b只有最右边一位为1。

以下为C语言代码实现

#include <stdio.h>
#include <stdlib.h>

typedef struct bucket
{
	int number;    //保存1的个数
	int timestamp;    //保存时间戳
	struct bucket *next;
}bucket,*pbucket;

int windows;    //窗口大小
int buckets;    //桶个数
int time;       //任意时间
int numbers;    //数据流
int count;      //精确计数

pbucket memory()
{
	int sign;    //用于保存读出来的数
	int i = 1;

	FILE *fp;

	pbucket p,q;
	pbucket h = NULL;

//	windows = 1000;
	count = 0;
	numbers = 0;

	printf("请输入窗口的大小\n");
	scanf("%d",&windows);
	printf("请输入查看的时间\n");
	scanf("%d",&time);

	fp = fopen("E:\\大数据\\实验1\\01stream.txt","r");

	while((feof(fp) == 0) && (numbers < time))    //读取查看时间之前的数据流
	{
		fscanf(fp,"%d",&sign);
//	    printf("sign = %d\n",sign);

		if(sign == 1)    //数据流为1入链表
		{
			if(numbers > (time - windows))    //用于精确统计
			{
				count++;
			}

			p = (pbucket)malloc(sizeof(bucket));    //存进来就申请一个节点
			p->timestamp = i;
			p->number = 1;
			q = p;
			if(h)
			{
				q->next = h;    //第一个直接插在h后面,其他使用头插法
			}
			if(!h)
			{
				p->next = NULL;
			}
			h = q;
			judge(h,1);
		}

		i++;    //用于记录时间戳
		numbers++;
	}

	return h;
}

int judge(pbucket h,int n)
{
	pbucket p,q,r;
	int i = 0;
	p = q = h;

//	r = p->next;
	while(p)
	{
		if(q)
		{
			if(q->number == n)
			{
				i++;
				if(i == 3)    //当有三个桶里面的数一样时,进行合并
				{
					r->number = n * 2;    //桶里面的数*2
					r->next = q->next;    //删除节点
					free(q);    //释放空间
					n *= 2;
					p = p->next;
					judge(p,n);
					break;
				}
				else
				{
					r = q;
					q = q->next;
				}
			}
			else
				break;
		}
		else
			break;
	}
}

void destory(pbucket *h)    //销毁链表
{
	pbucket p,q;
	p=*h;
	while(p)
	{
		q=p;
		p=p->next;
		free(q);
	}
	*h=NULL;
}

int main()
{
	int sum = 0;

	pbucket h,q;
	h = memory();
	q = h;

	buckets = 0;

	while(q)
	{
		if(q->timestamp > (time - windows))
		{
			if(q->next->timestamp <= (time - windows))
			{
				q->number /= 2;
			}
			sum += q->number;
			if(q->next->timestamp < (time - windows))
			{
				q->number *= 2;
			}
			printf("q->timestamp = %d\n",q->timestamp);
			printf("bucket = %d\n",q->number);
		    buckets++;
		}
		q = q->next;
	}

	printf("sum = %d\n",sum);
	printf("buckets = %d\n",buckets);
	printf("windows = %d\n",windows);
	printf("count = %d\n",count);

	destory(&h);

	return 0;
}



阅读更多
个人分类: 算法
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

大数据算法之——DGIM算法

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭