DGIM算法能够使用O(log2N)位的情况下表示N位窗口,将估计得错误率降到任意大于0得数。
流中每位都有时间戳,我们将窗口划分为多个桶,每个桶中包含
1、最右部得时间戳(最近的时间戳)
2、桶的大小(桶中1的个数,是2的幂)
此时桶必须遵循6条规则:
1、桶的最右部的位置总是1;
2、每个1的位置都在桶内、
3、一个位置只能属于一个桶
4、桶的大小从小到大,相同的桶的个数只有一到r个
5、从远到近扫描,桶的大小不会减少
算法过程,每次流中来1的时候,新建一个大小为1的桶,当相同桶的个数达到r+1个的时候,将最前面两个桶合为一个桶,并把过期的桶删除(时间戳大于当前时间-窗口大小)。当我要输出某时刻当前窗口1的个数,即将最早的桶的大小*50%+后面每个桶的大小。
此时误差:假设最左边桶的大小为2的j次(后面用2j代替吧),真实的至少为1+(r-1)(2的j-1次+2的j-2次+。。。+1)=(r-1)(2j - 1)。
最大误差: (2的j-1次-1)/(1+(r-1)(2的j次-1))
c语言实现,建立双向循环链表,用来存储桶
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define MaxSize 100010
using namespace std;
typedef struct node
{
int bsize;
int btime;
struct node *next;
struct node *pior;
}Bucket,*pBucket;
void addItem(pBucket h,int t) //添加新的桶
{
pBucket p;
p=(pBucket)malloc(sizeof(Bucket));
p->bsize=1;
p->btime=t;
p->next=h;
p->pior=h->pior;
h->pior->next=p;
h->pior=p;
}
void Delete(pBucket h,int t,int size_win) //删除过期桶
{
pBucket p;
while(1)
{
p=h->next;
if(p->btime<(t-size_win))
{
h->next=p->next;
p->next->pior=h;
free(p);
}
else
break;
}
}
void Merge(pBucket h,int max_num ) //合并
{
int i,t,size,same;
pBucket p,q;
q=h->pior;
while(q!=h)
{
same=1;
p=q;
for(i=1;i<=max_num;i++)
{
if(p->pior->bsize!=p->bsize || p->pior==h)
{
same=0;
break;
}
p=p->pior;
}
if(same==1)
{
for(i=0;i<max_num;i++) //找到要合并的桶
{
q=q->pior;
}
p=q->next; //找到要删除的桶
t=p->btime;
size=(p->bsize)*2;
p->pior->next=p->next;
p->next->pior=p->pior;
free(p);
/* for(i=1;i<max_num;i++)
{
p=q->pior;
p->pior->next=p->next;
p->next->pior=p->pior;
free(p);
}
*/
q->bsize=size;
q->btime=t;
}
else
break;
}
}
int Print_estimate(pBucket h,int max_num)
{
pBucket p;
int total_num=0;
p=h->next;
total_num+=p->bsize*(1.0/max_num);
printf("size : %d, time:%d\n",p->bsize,p->btime);
p=p->next;
while(p!=h)
{
printf("size : %d, time:%d\n",p->bsize,p->btime);
total_num+=p->bsize;
p=p->next;
}
printf("estimate total: %d\n",total_num);
return total_num;
}
int Prin_real(int window[],int size_win)
{
int i,num=0;
for(i=0;i<size_win;i++)
num+=window[i];
printf("real total:%d\n",num);
return num;
}
int main()
{
FILE *fp;
int temp;//临时变量,用来记录该时刻的数集,1或0
int t_now=0,t_need,max_num_buc;
int window[MaxSize]={0},size_win;
int est_num,real_num;
pBucket Buc;
Buc=(pBucket)malloc(sizeof(Bucket));
Buc->next=Buc;
Buc->pior=Buc; //头指针初始化
if(!(fp=fopen(".\\01stream.txt","r")))
printf("FAIL TO OPEN FILE");
printf("请输入滑动窗口大小:\n");
scanf("%d",&size_win);
printf("请输入想获取数据的时间:\n");
scanf("%d",&t_need);
printf("请输入精度:\n");
scanf("%d",&max_num_buc);
while(fscanf(fp,"%d",&temp)!=EOF && t_need!=t_now)
{
window[t_now%size_win]=temp;
if(temp==1)
{
addItem(Buc,t_now);//新建一个桶;
Delete(Buc,t_now,size_win);
Merge(Buc,max_num_buc );
}
t_now++;
}
fclose(fp);
est_num=Print_estimate(Buc,max_num_buc);
real_num=Prin_real(window,size_win);
printf("误差:%f\n",(float)(est_num-real_num)/(float)real_num);
return 0;
}