我试图在基于php的项目上集成FNV散列算法,作为为各种数据(如url、关键字)生成散列的一部分。
我看到了内文·博亚诺夫写的这篇文章。他提到,由于php中的算术限制,他被迫使用逐位移位和加法而不是乘法。他的实施是否正确?我在计算机科学领域的知识是有限的,所以我无法亲自验证。
我还有一个问题是FNV的不同“口味”。我看到它提供了32位、64位和128位变量,但是使用上面的实现,我总是得到8个字符的十六进制散列(我使用dechex()将整数结果转换为十六进制)。
输入“Lorem ipsum dolor sit amet,consectetur adipising elit.我得到了以下十六进制结果:
(32位偏移量)5B15C0F2
(64位偏移量)6EA33CB5
为什么会这样?我期望64位FNV的十六进制结果为16个字符。“味道”是否只指将要使用的算术运算和种子,而不是结果的长度?(即,如果我说64位fnv,哈希函数将使用64位操作和种子,但结果仍然是32位)
一点启发会非常感激:)
最佳答案
我很久以前就编写了php fnv散列函数,它是为了一个特殊的目的,所以当时32位的实现就足够了。
为了回答您的第一个问题,通过比较算法(代码)和示例结果,实现了对其他(C和C++)实现的测试。所以对于32位的结果,它可以正常工作。
如果要自己实现64位(或128位)版本,则应首先更改fnv_offset_基础,同时更改第73行上的表达式,该表达式当前为:
$hash += ($hash<<1) + ($hash<<4) + ($hash<<7) + ($hash<<8) + ($hash<<24);
…这相当于乘以16777619(fnv_prime_32),二进制数为1000000000000011000110-分解为这个表达式:
2^24 + 2^8 + 2^7 + 2^4 + 2^1 + 2^0
。对于64位,应乘以109951162811-二进制1000000000000000000000000110110011…表达式:
2^88 + 2^8 + 2^7 + 2^5 + 2^4 + 2^1 + 2^0
。我不知道php将如何处理表达式
$hash << 88
,但您应该自己试验一下。在我的php 5.2.x中,对于大于31的数字,它不能很好地工作。最后,您可能需要修改
$hash = $hash & 0x0ffffffff;
以从结果中移除一些垃圾。我通过实验发现了这一点。对于64位OT应该是$hash = $hash & 0x0ffffffffffffffff;
。验证它是否与php一起正常工作。您还可以使用其他php库来获得更高的算术精度。在我看来,按位移位更快。
事实上,可以为任意位数生成fnv散列。