您如何解释我的 C 哈希函数(Fowler–Noll–Vo_hash_function 类型)的行为?
How would you interpret the behaviour of my C Hash function (type of Fowler–Noll–Vo_hash_function)?
我不明白为什么整数值 "hash" 越来越低 in/after 3 循环。
我猜这是因为 uint 限制是 2,147,483,647
。
但是...当我尝试逐步进行时,该值等于 2146134658
?。
我数学不太好,但应该低于限制。
#define FNV_PRIME_32 16777619
#define FNV_OFFSET_32 2166136261U
unsigned int hash_function(const char *string, unsigned int size)
{
unsigned int str_len = strlen(string);
if (str_len == 0) exit(0);
unsigned int hash = FNV_OFFSET_32;
for (unsigned int i = 0; i < str_len; i++)
{
hash = hash ^ string[i];
// Multiply by prime number found to work well
hash = hash * FNV_PRIME_32;
if (hash > 765010506)
printf("YO!\n");
else
printf("NOO!\n");
}
return hash % size;
}
如果你想知道这个 if 语句只适合我。
if (hash > 765010506)
printf("YO!\n");
else
printf("NOO!\n");
765010506
是下一个 运行 循环后的散列值。
I would guess this happen because the uint limitation is 2,147,483,647.
32 位 无符号 整数的最大值大约为 40 亿 (232 - 1 = 4,294,967,295)。你想到的数字是 signed 整数的最大值 (231 - 1).
2,146,134,658 略小于 231(因此它甚至可以放入无符号 32 位整数),但它仍然非常接近极限。将它乘以 FNV_PRIME_32
—— 大约是 224 —— 将得到大约 255 的结果,这将导致溢出.
I dont understand why the interger Value "hash" is getting lower in/after the 3 loop.
C 中的所有无符号整数运算都是 modular arithmetic。对于unsigned int
,就是模UINT_MAX + 1
;对于 unsigned long
、modulo ULONG_MAX + 1
,等等。
(a
modulo m
表示 a
除以 m
的余数;在 C 中,a % m
如果 a
和 m
都是无符号整数类型。)
在许多当前的体系结构中,unsigned int
是一个 32 位无符号整数类型,UINT_MAX == 4294967295
。
让我们看看这在实践中意味着什么,对于乘法(乘以 65520,这恰好是一个有趣的值;216 - 16):
unsigned int x = 1;
int i;
for (i = 0; i < 10; i++) {
printf("%u\n", x);
x = x * 65520;
}
输出为
1
65520
4292870400
50327552
3221291008
4293918720
16777216
4026531840
0
0
What? How? How come the result ends up zero? That cannot happen!
当然可以。事实上,您可以从数学上证明它最终会在乘数为偶数时发生,并且模数是关于 2 的幂(此处为 232)。
但是,您的特定乘数是奇数;因此,它不会受到上述影响。然而,由于模运算,它仍然环绕。如果我们用您的乘数 16777619
和更长一点的序列
重试相同的操作
unsigned int x = 1;
int i;
for (i = 0; i < 20; i++) {
printf("%u\n", x);
x = x * 16777619;
}
我们得到
1
16777619
637696617
1055306571
1345077009
1185368003
4233492473
878009595
1566662433
558416115
1485291145
3870355883
3549196337
924097827
3631439385
3600621915
878412353
2903379027
3223152297
390634507
事实上,这个序列有 1,073,741,824 次迭代(在它自己重复之前),并且永远不会产生 0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 14 , 或 15,例如——也就是说,如果它从 1 开始。它甚至需要 380 次迭代才能得到小于 16,777,619 (16,689,137) 的结果。
对于哈希函数,没关系。每个新的非零输入都会改变状态,因此序列不是 "locked"。但是,没有理由期望散列值随着散列数据长度的增加而单调增加;最好假设它是 "roughly random":不是真正随机的,因为它仅取决于输入,但也不是很明显的规则。
我不明白为什么整数值 "hash" 越来越低 in/after 3 循环。
我猜这是因为 uint 限制是 2,147,483,647
。
但是...当我尝试逐步进行时,该值等于 2146134658
?。
我数学不太好,但应该低于限制。
#define FNV_PRIME_32 16777619
#define FNV_OFFSET_32 2166136261U
unsigned int hash_function(const char *string, unsigned int size)
{
unsigned int str_len = strlen(string);
if (str_len == 0) exit(0);
unsigned int hash = FNV_OFFSET_32;
for (unsigned int i = 0; i < str_len; i++)
{
hash = hash ^ string[i];
// Multiply by prime number found to work well
hash = hash * FNV_PRIME_32;
if (hash > 765010506)
printf("YO!\n");
else
printf("NOO!\n");
}
return hash % size;
}
如果你想知道这个 if 语句只适合我。
if (hash > 765010506)
printf("YO!\n");
else
printf("NOO!\n");
765010506
是下一个 运行 循环后的散列值。
I would guess this happen because the uint limitation is 2,147,483,647.
32 位 无符号 整数的最大值大约为 40 亿 (232 - 1 = 4,294,967,295)。你想到的数字是 signed 整数的最大值 (231 - 1).
2,146,134,658 略小于 231(因此它甚至可以放入无符号 32 位整数),但它仍然非常接近极限。将它乘以 FNV_PRIME_32
—— 大约是 224 —— 将得到大约 255 的结果,这将导致溢出.
I dont understand why the interger Value "hash" is getting lower in/after the 3 loop.
C 中的所有无符号整数运算都是 modular arithmetic。对于unsigned int
,就是模UINT_MAX + 1
;对于 unsigned long
、modulo ULONG_MAX + 1
,等等。
(a
modulo m
表示 a
除以 m
的余数;在 C 中,a % m
如果 a
和 m
都是无符号整数类型。)
在许多当前的体系结构中,unsigned int
是一个 32 位无符号整数类型,UINT_MAX == 4294967295
。
让我们看看这在实践中意味着什么,对于乘法(乘以 65520,这恰好是一个有趣的值;216 - 16):
unsigned int x = 1;
int i;
for (i = 0; i < 10; i++) {
printf("%u\n", x);
x = x * 65520;
}
输出为
1
65520
4292870400
50327552
3221291008
4293918720
16777216
4026531840
0
0
What? How? How come the result ends up zero? That cannot happen!
当然可以。事实上,您可以从数学上证明它最终会在乘数为偶数时发生,并且模数是关于 2 的幂(此处为 232)。
但是,您的特定乘数是奇数;因此,它不会受到上述影响。然而,由于模运算,它仍然环绕。如果我们用您的乘数 16777619
和更长一点的序列
unsigned int x = 1;
int i;
for (i = 0; i < 20; i++) {
printf("%u\n", x);
x = x * 16777619;
}
我们得到
1
16777619
637696617
1055306571
1345077009
1185368003
4233492473
878009595
1566662433
558416115
1485291145
3870355883
3549196337
924097827
3631439385
3600621915
878412353
2903379027
3223152297
390634507
事实上,这个序列有 1,073,741,824 次迭代(在它自己重复之前),并且永远不会产生 0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 14 , 或 15,例如——也就是说,如果它从 1 开始。它甚至需要 380 次迭代才能得到小于 16,777,619 (16,689,137) 的结果。
对于哈希函数,没关系。每个新的非零输入都会改变状态,因此序列不是 "locked"。但是,没有理由期望散列值随着散列数据长度的增加而单调增加;最好假设它是 "roughly random":不是真正随机的,因为它仅取决于输入,但也不是很明显的规则。