【问题标题】:Hashing for array indices数组索引的散列
【发布时间】:2018-02-01 00:38:56
【问题描述】:

在我的 C 程序中,我在一个结构中分配了四个 8 位 (char) 变量。如果我想对这些数字进行哈希处理以创建索引数组的键(代表整个结构),我该怎么办? (在程序中有很多这样的结构;因为我经常需要在符号表中搜索它们是否存在,如果我不想创建其他的,我不知道使用哪种哈希算法,如果我'想要进行键索引搜索)。

我想到了一种散列方法,它取四个数字,将它们转换为十六进制数字,将它们连续放置,然后将输出的数字转换为十进制数字。

但我需要一些不那么“沉重”的东西……这种方法似乎太虚荣了,我认为它不太适合创建数组索引。

是吗?如果可能的话,是否还有另一种哈希函数,它占用的内存也少于 32 位?

【问题讨论】:

    标签: c hash


    【解决方案1】:

    你可能想看看这个list of hash functions

    为了实现哈希表(我想这是您的目标),您需要一个带有 avalanche effect 的哈希函数,以避免类似输入值出现过多的哈希冲突。

    当然,您可以使用任何函数将字符转换为任意整数表示,但如果此表示对于不同的输入没有变化,则您实际上具有链表的性能(想象一下使用其他建议之一表大小为 256,并且没有任何结构在字节 4 上变化)。您对 32 位哈希有什么顾虑?您当然会使用hash%tablesize 进行索引?

    通常您也不会使用加密哈希函数(例如 md5、sha-1)。只需选择一种非加密哈希函数(例如 Pearson/Jenkins 哈希)。

    /* jenkins hash, copied from http://en.wikipedia.org/wiki/Jenkins_hash_function */
    uint32_t jenkins_one_at_a_time_hash(char *key, size_t len)
    {
      uint32_t hash, i;
      for(hash = i = 0; i < len; ++i)
      {
        hash += key[i];
        hash += (hash << 10);
        hash ^= (hash >> 6);
      }
      hash += (hash << 3);
      hash ^= (hash >> 11);
      hash += (hash << 15);
      return hash;
    }
    

    旁注:当你有一个良好的哈希值分布时,还要确保哈希表的大小足够大。当数组的占用率(负载因子)接近 1 时,您会观察到性能下降,因为哈希冲突的可能性会增加。

    【讨论】:

      【解决方案2】:

      一种可能性(我认为 OP 没有描述)是将 4 个 char 值组合成一个 32 位整数,然后使用哈希表的大小(可能是质数)对其进行修改:

      unsigned int combined = (c1 << 24 ) | (c2 << 16 ) | (c3 << 8 ) | (c4);
      unsigned int hashval = combined % hashtablesize;
      

      这当然取决于 4 个单独字节的实际预期值,但这种类型的哈希相当有效,并且通常具有良好的分布。最好使用预期的数据集测试生成的哈希值,以确保分布有点均匀。

      【讨论】:

        【解决方案3】:

        为什么不把结构放在一个数组中?

        #include <stdio.h>
        
        typedef struct {
          char a,b,c,d;
        } item;
        item items[20];
        
        int main(int argc, char *argv[])
        {
          items[0].a = 4;
          items[0].b = 6;
          items[0].c = 1;
          items[0].d = 3;
          // ...
          items[4].a = 12;
          // ...
          printf("%d %d %d %d\n", items[0].a, items[0].b, items[0].c, items[0].d);
          return 0;
        }
        

        显然,这是内存占用较少的解决方案,因为数据直接存储在主数组中,因此不需要散列索引,因为数组的索引可以在不消耗内存的情况下完成这项工作。

        当然你可以使用指针、一些 C++ 向量功能等。但这是最简单有效的方法。

        唯一需要注意的是,您必须知道数组的大小(您将拥有多少项)或不超过 XXX 的最大值...

        【讨论】:

          【解决方案4】:

          是否还有另一种哈希函数,它也占用更少的内存 超过 32 位,如果可能的话?

          这是一个虚幻的问题。键是一个数组索引——它不存储在任何地方,它是在查找时计算的。 C 中的数组是连续的块,根据数组的开头和类型的大小乘以索引来访问各个元素。

          对于键,只需将值转换为无符号的 32 位类型(不要只使用 intunsigned int,因为大小不一定是 32 位):

          #include <inttypes.h>
          char x[4] = { 'A', 'B', 'C', 'D' };
          uint32_t *key = (uint32_t*)&x;        
          

          然后根据表格大小做模数。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-08-29
            • 2016-02-16
            • 2010-11-01
            • 2014-01-27
            • 2014-01-04
            • 2012-03-14
            • 2010-12-31
            • 2011-01-15
            相关资源
            最近更新 更多