使用Rabin-Karp算法对文件进行切片

3
我写了一个C程序,用Rabin Karp算法将文件切成块。这是一个改编自C#程序的版本,你可以在这里找到它。
看起来它能工作,但问题仍然存在。平均块大小不是预期的。
使用方法如下:
rabin Prime WindowSize BoundaryMarker File 其中:
Rabin是可执行文件的名称。
Prime是一个高质数,例如100007。
WindowSize是滚动窗口的大小,例如48。
BoundaryMarker是指纹中设置为0的位数。
File是要处理的文件。
如果我将BoundaryMarker设置为13,则预计平均块大小为8K。实际上,它们都不在8K左右。
我很难弄清楚我的程序出了什么问题?你能帮我吗?
谢谢
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

unsigned char* buffer;
int windowSize;
int writePointer = 0;
int readPointer = 0;
int dataSize = 0;

unsigned char PushChar(unsigned char c)

{ if (++writePointer >= windowSize) writePointer=0;
  buffer[writePointer]=c;
  dataSize++;
  return(c);
}

unsigned char PopChar(void)

{ if (++readPointer >= windowSize) readPointer=0;
  dataSize--;
  return(buffer[readPointer]);
}


int main(int argc, char *argv[])

{ int fd;
  unsigned char c;

  unsigned long Q;
  unsigned long D=256;
  unsigned long pow=1;
  int i,k,boundary,boundaryMarker,index;
  unsigned char s; 

  if (argc != 5) 
  { printf("\nUsage : rabin Prime WindowSize BoundaryMarker File\n\nwhere :\n");
    printf("Prime is a high prime number. For instance 100007\n\n");
    printf("WindowSize is the size of rolling window. For instance 48\n\n");
    printf("BoundaryMarker is the number of bits set to 0 in a fingerprint\n\n");
    printf("File is the file to process\n\n");
    return(1);
  }

  sscanf(argv[1],"%lu",&Q);
  sscanf(argv[2],"%d",&windowSize);
  sscanf(argv[3],"%d",&boundaryMarker);

  for(i=1,boundary=1;i<=boundaryMarker;i++) boundary=boundary*2;
  boundary --;

  //printf("Q = %lu windowSize = %d boundary = %d\n",Q,windowSize,boundary);

  if ((buffer=(unsigned char*) malloc (sizeof(unsigned char)*windowSize))==NULL) return(1);

  for (k=1; k < windowSize; k++) pow=(pow*D)%Q;
  //printf("pow value %lu\n",pow);

  unsigned long sig=0;
  int lastIndex=0;

  if ((fd=open(argv[4],O_RDONLY))<0) exit(1);

  for (i=0; i <windowSize; i++)
  { read(fd,&c,1);
    PushChar(c);
    sig=(sig*D + (unsigned long)c) %Q;
  }

  //printf("sig value = %lu\n",sig);

  index=0; lastIndex=0;

  while (read(fd,&c,1))
  { 
    s=PopChar();
    //printf("sig = ( %lu + %lu - %lu * %lu %% %lu ) %lu",sig,Q,pow,(unsigned long) s,Q,Q);
    sig = (sig + Q - pow*(unsigned long)s%Q)%Q;
    //printf(" = %lu\n",sig);
    s=PushChar(c);
    //printf("sig2 = ( %lu * %lu + %lu ) %% %lu",sig,D,(unsigned long) s,Q);
    sig = (sig*D + (unsigned long)s)%Q;
    //printf(" = %lu\n",sig);
    index++;
    if ((sig & boundary )==0)
       { if (index - lastIndex >= 2048)
         { printf("sig & boundary = %lu & %lu Index=%d chunk size=%d\n",sig,boundary,index,index-lastIndex);
           lastIndex=index;
     }
       }
    else if (index -lastIndex >=65536)
            { printf("sig & boundary = %lu & %lu Index=%d chunk size=%d\n",sig,boundary,index,index-lastIndex);
              lastIndex=index;
            }
  }
  printf("Index=%d chunk size=%d\n",index,index-lastIndex);

  close(fd);
  return 1;
}

你可以使用调试器逐步执行代码,并关注变量及其值。这可能有助于找出问题所在。 - Some programmer dude
两个程序(C和C#)给出了相同的结果。我认为这是一个算法问题。该算法看起来像是Sedgewick Rabin Karp实现。我不知道问题出在哪里。 - Jean Labiche
2个回答

0
使用BoundaryMarker = 13,在一个兆字节的随机数据上运行您的代码,给出了104个块,平均块大小为10082字节。这与期望值8192相差不远。
然而,较小的BoundaryMarker值显示出更明显的偏差;例如将其设置为10,平局块大小为3049字节,距离预期的1024相差很远。而设置BoundaryMarker = 5则产生了平均块大小为2077字节,根本无法接近32字节的预期大小。
仔细查看代码,造成这种偏差的明显原因在于以下代码(为了清晰起见进行改格式):
if ((sig & boundary ) == 0)
{ if (index - lastIndex >= 2048)
  { printf("sig & boundary = %lu & %lu Index=%d chunk size=%d\n",sig,boundary,index,index-lastIndex);
    lastIndex=index;
  }
}
else if (index - lastIndex >= 65536)
{ printf("sig & boundary = %lu & %lu Index=%d chunk size=%d\n",sig,boundary,index,index-lastIndex);
  lastIndex=index;
}
if (index - lastIndex >= 2048)这行代码会忽略小于2048字节的块边界,将短于2048字节的块与后面的块合并。而else if (index - lastIndex >= 65536)则会强制插入一个人工块边界,以防止任何块超过65536字节。如果您不想这样做(即强制所有块长度都在2048到65536字节之间),只需删除这些检查即可简化代码。
if ((sig & boundary ) == 0)
{ printf("sig & boundary = %lu & %lu Index=%d chunk size=%d\n",sig,boundary,index,index-lastIndex);
  lastIndex=index;
}

实际上,进行此更改会使得BoundaryMarker = n的平均块大小非常接近于2^n字节,至少对于n≤12左右。

对于n=13,似乎存在明显的向下偏差,我怀疑这是因为质数100007仅约为边界模数2^13的12.2倍所致。由于签名值在模质数意义下更或多或少地随机分布,这额外的0.2导致它们在进一步减小模2^13时略微偏向较小的值(包括零)。

可以通过使用更大的质数来轻松解决这种偏差,例如2^31−1 = 2147483647。实际上,切换到此质数使平均块大小更接近8192。


-1

您可以尝试更新BoundaryMarker值,以获得不同的长度。我已经使用RB这种方式:github链接。而且我认为长度实际上取决于内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接