小阵列上最有效的搜索?

时间:2014-11-04 22:32:17

标签: c arrays search

我有一个C数组,很少(几乎从未)更新过:

unsigned long user_values[8];

我希望能够进行大量快速查找(在慢速机器上每秒数百次),检查数组中是否有值,如果是,则获取其索引。几乎所有的时候,这个查找都无法在数组中找到该项。

通常,我会保持数组排序,并使用二进制搜索,但我不确定二进制搜索在非常小的数据集上的效率。有没有更有效的方法来执行此查找,因为它的已知大小很小?

4 个答案:

答案 0 :(得分:3)

我建议使用小哈希来立即检测大多数故障。像这样:

#define HASH(x)    ((x) & 0xff)   // This can be improved if needed
uint8_t possible[256];

void initHash()
{
    int i;

    memset(possible, 0, sizeof(possible));
    for (i=0;i<8;i++)
        possible[HASH(user_values[i])] = 1;
}

int find(unsigned long val)
{
    // Rule out most failures with a quick test.
    if (!possible[HASH(val)])
        return -1;

    // Now use either binary or linear search.
    ...
}

请注意,在256个插槽哈希表中设置最多8个插槽,您将立即清除31/32或97%的故障。有三种明显的方法可以改善这种情况:

  1. 您可以使哈希表更大,以过滤掉更多故障,但代价是使用更多内存。更好的是,您可以添加第二个哈希值(例如,第二个到最后一个字节的哈希值)。这只需要另外256个字节,但过滤掉了传递第一个哈希值的3%的97%。顺便说一下,这种多个哈希算法称为布隆过滤器。
  2. 每个哈希索引可以使用1位而不是一个字节,这使得哈希表的大小为1/8,但需要更长的计算才能进行哈希检查。
  3. 您可以提供更好的哈希函数,具体取决于您期望的数据类型。

答案 1 :(得分:1)

你想要多少加速?除非你的集合(数组)中有一些可利用的模式,只需8次检查,否则无论如何都会获得最小的收益。编译器通常非常擅长优化这类事情。我发现使用一些gcc编译,在我的for循环中使用指针可以给我几个百分点。展开循环,因为你知道静态8偏移可能值几个百分点(也可能不是)。

这是一个假设的可利用数据模式。如果您的集合/矢量/列表/数组/调用它们将倾向于聚类并且您要测试的候选范围均匀分布在0x00000000到0xFFFFFFFF范围内,那么您可能获得很少有你的矢量预分类,只是测试少于第一个或大于最后一个,这将是2个测试,可能通常失败,当它没有时,转移到列表中的线性搜索。但是这种事情实际上取决于各种比例(窗口有多宽?预分类增加多少开销等等)。只针对您的真实世界数据进行测试即可。

并且总是存在真正的危险,可利用的模式在正常情况下提供20%的速度时,在你的假设被违反的边缘情况下表现得非常糟糕,并且会受到数量级的伤害。

答案 2 :(得分:1)

switch-case jump table有一些技巧,但它要求编译器利用它,在您的特定情况下可能会或可能不会发生。而不是将这些值保留在数组中(因为你写的值几乎从未改变),将它们字面上称为标签:

#define NOT_FOUND -1

int index = NOT_FOUND; // or any other way to mark that number is not found
switch (val)
{
    case 0x00000000UL : // replace with array values
        index = 0; break;
    case 0x00000001UL :
        index = 1; break;
    case 0x00000002UL :
        index = 2; break;
    // ...
};

唯一的缺点是数字现在已经修复了#34;在编译时。因此,要更新它们,您需要重新编译整个程序,这可能是不可接受的(?)。

答案 3 :(得分:1)

该程序进行二分搜索和线性搜索(很多次,因此可以很容易地定时)。在OP的条件下,通常找不到搜索值,线性搜索大约需要二进制搜索的两倍。它需要3次迭代的二分搜索才能将8个元素减少到1,但是线性搜索的8次迭代。我使用unsigned int而不是unsigned long

#include <stdio.h>
#include <time.h>

#define ARRLEN 8
#define LOOPS 0x7FFFFF

unsigned user_values[ARRLEN] = { 1, 234, 8124, 8335, 10234, 11285, 637774, 788277 };

int main (int argc, char *argv[]) {
    unsigned val;
    int bot, top, mid, loop;
    clock_t start, elap;

    if (argc < 2) return 1;
    if (sscanf (argv[1], "%u", &val) != 1) return 1;

    // binary search
    printf ("Binary search for %u: ", val);
    start = clock();
    for (loop=0; loop!=LOOPS; loop++) {
        bot = 0;
        top = ARRLEN;
        while (top-bot > 1) {
            mid = ((top + bot) >> 1);
            if (user_values[mid] <= val)
                 bot = mid;
            else top = mid;
        }
    }
    elap = clock() - start;
    if (user_values[bot] == val)
         printf ("found");
    else printf ("not found");
    printf (" in count %u\n", (unsigned)elap);

    // linear search
    printf ("Linear search for %u: ", val);
    start = clock();
    for (loop=0; loop!=LOOPS; loop++) {
        for (bot=0; bot<ARRLEN; bot ++)
            if (user_values[bot] == val)
                break;
    }
    elap = clock() - start;
    if (bot<ARRLEN)
         printf ("found");
    else printf ("not found");
    printf (" in count %u\n", (unsigned)elap);

    return 0;
}