16位唯一键到8位数组索引(完美哈希)

时间:2018-06-20 19:28:56

标签: c hashmap embedded perfect-hash

我有一组由uint16_t唯一标识的结构。这些结构永远不会超过256个(由于我不会进入uint16_t的原因,因此必须使用它们来识别这些结构)。

我想通过指针数组存储这些结构。由于永远不会有超过256个结构,因此我想静态分配一个大小为256的结构指针数组。尽管如此,我需要一个函数将uint16_t唯一地映射到uint8_t。

鉴于我将在运行时知道所有键(尽管在运行时之前我不会知道),是否存在一种算法,该算法通常会给我唯一的映射(即完美的哈希)?

一个警告是我正在使用的系统具有16位地址。因此,出于效率考虑,我不想使用任何大于uint16_t的类型。

2 个答案:

答案 0 :(得分:1)

  

鉴于我将在运行时知道所有键(尽管我不知道   在运行前)是否存在一种算法,该算法将为我提供   唯一的映射(即完美哈希)?

鉴于您最多可以映射256个(16位)值,原则上可以使用许多映射。但是,如果要支持的密钥不相关,则任何用于计算映射的算法都需要全部256个密钥或它们的功能作为参数。例如,在评论中,讨论了256 次多项式的概念,并且参数将包含多项式的系数。

现在考虑到,由于映射需要256个参数,因此它将以某种方式使用所有这些参数。那么,具有这些一般特征的事物如何有效地计算?

对于不相关的键,我看到的最佳解决方案是将所有键放入数组中,对其进行排序,然后将排序后的数组中每个键的索引用作所需的哈希值。 (因此,在这种情况下,参数本身就是键。)您可以通过二进制搜索相当有效地计算那些索引。假设您在程序执行过程中存储了排序后的数组,那么我认为您没有比这种方法更有效地进行任何此类计算的过程,而且它非常简单,因此您可以确信其正确性。

假定您需要先哈希所有键,然后再对其进行哈希处理。如果不是这种情况,那么至少可以使用未排序的数组和线性搜索(尽管也可能存在中间方法)。线性搜索看似效率不是很高,但平均而言,它不比包含256个参数的纯算术计算差。

答案 1 :(得分:-1)

我最终使用第一个拟合算法将16位值唯一地映射到8位值(在不超过256个16位值的假设下工作)。下面是一个非常简短的示例,我编写了代码对其进行测试。虽然映射函数非常昂贵(以下称为创建映射),但get_value函数是恒定的。因此,一旦建立了映射,就应该很快地计算出哈希值(在我的示例中为guster + offset [divisor])并获得关联的值。

uint16_t keys[256];
uint16_t actual_mapping[256];
uint8_t offset[256];
uint8_t num_keys = 0;

void 
create_mapping()
{
    uint8_t mapping_matrix[num_keys][2];

    uint8_t index;
    uint8_t test_index;
    for(index = 0; index < num_keys; index++)
    {
        mapping_matrix[index][0] = (uint8_t) (keys[index] / 256);
        mapping_matrix[index][1] = keys[index] % 256;
    }

    for(index = 0; index < num_keys - 1; index++)
    {
        uint8_t hash_not_found = 1;
        while(hash_not_found)
        {
            hash_not_found = 0;
            for(test_index = index + 1; test_index < num_keys; test_index++)
            {
                if(mapping_matrix[index][0] != mapping_matrix[test_index][0])
                {
                    if((uint8_t) (mapping_matrix[index][1] + offset[mapping_matrix[index][0]]) == (uint8_t) (mapping_matrix[test_index][1] + offset[mapping_matrix[test_index][0]]))
                    {
                        hash_not_found = 1;
                        offset[mapping_matrix[index][0]]++;
                        break;
                    }
                }
            }
        }

        actual_mapping[(uint8_t) (mapping_matrix[index][1] + offset[mapping_matrix[index][0]])] = keys[index];
    }
}

uint16_t
get_value(uint16_t value)
{
    uint8_t divisor = (uint8_t) (value / 256);
    uint8_t remainder = value % 256;
    return actual_mapping[(uint8_t) (remainder + offset[divisor])];
}

int main(int argc, char** argv) {

    keys[0] = 0;
    keys[1] = 256;
    keys[2] = 4;
    keys[3] = 13000;
    keys[4] = 14000;
    keys[5] = 15000;
    keys[6] = 16000;
    keys[7] = 3500;
    keys[8] = 69;
    keys[9] = 15;
    keys[10] = 16;
    keys[11] = 789;
    keys[12] = 12001;

    num_keys = 13;

    create_mapping();

    uint8_t index;
    for(index = 0; index < num_keys; index++)
    {
        printf("%hu\n", get_value(keys[index]));
    }  


}