在我的C程序中,我在结构中分配了四个8位( char )变量。如果我想散列这些数字以创建将索引数组的键(代表整个结构),我该怎么办? (在程序中有很多这样的结构;因为我经常需要在符号表中搜索它们是否存在,如果我不想创建其他结构,我还不知道要使用哪种哈希算法,如果我我想进行密钥索引搜索。
我考虑过一种散列,它取四个数字,用十六进制数字转换它们,连续放入它们,然后将出现的数字转换为十进制数字。
但是我需要一些不那么“沉重”的东西......这种方法看起来太虚荣了,我认为它不适合创建数组索引。
是吗?是否有其他类型的哈希函数,如果可能的话,它还占用的内存少于32位?答案 0 :(得分:2)
一种可能性(我认为OP没有描述)将4个char值组合成一个32位整数,然后使用哈希表的大小(可能是素数)来修改它:
unsigned int combined = (c1 << 24 ) | (c2 << 16 ) | (c3 << 8 ) | (c4);
unsigned int hashval = combined % hashtablesize;
当然,它取决于4个单独字节的实际预期值,但这种类型的散列相当有效,并且通常具有良好的分布。最好使用预期的数据集测试生成的哈希值,以确保分布有点均匀。
答案 1 :(得分:2)
您可能需要查看此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 :(得分:0)
为什么不将结构放在数组中?
#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的......
答案 3 :(得分:0)
是否有其他类型的哈希函数,它也占用更少的内存 超过32位,如果可能的话?
这是一个虚幻的问题。关键是数组索引 - 它不存储在任何地方,它是在查找时计算的。 C中的数组是连续的块,根据数组的开头和类型的大小乘以索引来访问单个元素。
对于密钥,只需将值转换为无符号32位类型(不要只使用int
或unsigned int
,因为大小不一定是32位):
#include <inttypes.h>
char x[4] = { 'A', 'B', 'C', 'D' };
uint32_t *key = (uint32_t*)&x;
然后根据表格大小做模数。