如何改进此字符计数算法

How to improve this character count algorithm

本文关键字:算法 字符 何改进      更新时间:2023-10-16

我想构造一个函数,该函数执行文件分析,从0x0到0xff的每个字节计数及其频率在数组中返回

所以,我写了这个原型:

// function prototype  and other stuff
unsigned int counts[256] = {0}; // byte lookup table 
FILE * pFile;                   // file handle
long fsize;             // to store file size
unsigned char* buff;            // buffer
unsigned char* pbuf;            // later, mark buffer start
unsigned char* ebuf;            // later, mark buffer end
if ( ( pFile = fopen ( FNAME , "rb" ) ) == NULL )
{
    printf("Error");
    return -1;
}
else
{
    //get file size
    fseek (pFile , 0 , SEEK_END);
    fsize = ftell (pFile);
    rewind (pFile);
    // allocate space ( file size + 1 )
    // I want file contents as string for populating it
    // with pointers
    buff = (unsigned char*)malloc( sizeof(char) * fsize + 1 );
    // read whole file into memory
    fread(buff,1,fsize,pFile);
    // close file
    fclose(pFile);
    // mark end of buffer as string
    buff[fsize] = '';
    // set the pointers to beginning and end
    pbuf = &buff[0];
    ebuf = &buff[fsize];

            // Here the Bottleneck
    // iterate entire file byte by byte
            // counting bytes 
    while ( pbuf != ebuf)
    {
        printf("%cn",*pbuf);
                    // update byte count
        counts[(*pbuf)]++;
        ++pbuf;                             
    }

    // free allocated memory
    free(buff);
    buff = NULL;
}
// printing stuff

但这种方式较慢。我正在寻找相关的算法,因为我已经看过 HxD 例如做得更快。

我认为也许一次读取一些字节可能是一种解决方案,但我不知道如何。

我需要帮助或建议。

谢谢。

假设你的文件不是太大,它会导致系统开始分页,因为你正在将整个东西读入内存,你的算法与通用数据一样好 - O(n)

你需要删除printf(如上所述(;但除此之外,如果性能不高于提高它的唯一方法,那就是查看生成的汇编程序 - 可能编译器没有优化所有的取消引用(GCC应该这样做(。

如果您碰巧对数据集有所了解,那么可能会有改进 - 如果它是一个位图类型的图像,可能具有相同的字节块,那么可能值得做一点运行长度编码。也可能有一些数据集实际上值得首先对数据进行排序(尽管这将一般情况减少到O(nlog(n)),所以不太可能。

RLE 看起来像(未经测试,可能是我头顶上的次优免责声明(

unsigned int cur_count=1;
unsigned char cbuf=*(++pbuf);
while ( pbuf != ebuf)
{
    while( pbuf != ebuf && cbuf == *pbuf )
    {
        cur_count++;
        pbuf++;
    }  
    counts[cbuf]+=cur_count;
    cur_count=0;                             
}
counts[cbuf]+=cur_count;

您通常可以通过增加程序大小来换取速度的提高,我认为这在您的情况下可以很好地工作。我会考虑用无符号的短*指针替换您的无符号字符*指针,并一次有效地处理两个字节。这样,您就有一半的数组索引增量、一半的偏移量计算到累加器中、一半的累加数量和一半的测试数量来查看循环是否已完成。

就像我说的,这将以增加程序大小为代价,所以你的累加器阵列现在需要 65536 个元素而不是 256 个,但这是一个很小的代价。我承认在易读性方面也存在权衡。

最后,您必须对我新的、更大的累加器的所有 65536 个元素运行索引,并用 0xff 屏蔽它以获得第一个字节并移位 8 位以获得第二个字节。然后,您将拥有与原始累加器对应的两个索引,您可以从那里将 2 个累加器累加到原始 256 累加器中。

附言请注意,虽然您一次可以处理几乎所有文件 2 个字节,但如果您的文件大小是奇数字节,则必须自行处理最后一个字节。

P.P.S. 请注意,这个问题很容易跨 4 个线程并行化,如果你想让你的备用 3 个 CPU 内核做一些比摆动拇指更有用的事情;-(