我有一个非常具体的问题:
我在15x50网格上有均匀的随机值,我想要散列的样本对应于以任何可能的网格位置为中心的5x5单元格的正方形。
因此,样本数量可以从25(远离边界,大多数情况下)到20,15(靠近边界)下降到最小值9(在一个角落里)。
因此,即使单元格值是随机的,该位置也会引入序列长度的确定性变化。
哈希表大小是一个小数字,通常在50到20之间。
该功能将对大量随机生成的网格(几百/千)进行操作,每个网格可能会被调用几千次。网格上的位置可以被认为是随机的。
我想要一个能够尽可能均匀地传播15x50个可能样本的函数。
我尝试过以下伪代码:
int32 hash = 0;
int i = 0; // I guess i could take any initial value and even be left uninitialized, but fixing one makes the function deterministic
foreach (value in block)
{
hash ^= (value << (i%28))
i++
}
hash %= table_size
但结果虽然并非严重失衡,但对我来说似乎并不顺利。也许是因为样本太小,但是情况使得难以在更大的样本上运行代码,如果有一些计算机知识为我准备好答案,我宁愿不必编写完整的测试工具。 :)
我不确定将值二乘二并且使用通用字节散列策略将是最佳解决方案,尤其是因为值的数量可能是奇数。
我已经尝试使用第17个值来表示离网细胞,但这似乎引入了偏差(来自边界附近的细胞的序列将具有很多&#34;关闭网格&#34;值)。
我不确定测试各种解决方案效率的最佳方法是什么(例如,我应该生成多少网格来了解性能)。
答案 0 :(得分:5)
http://www.partow.net/programming/hashfunctions/
这里有来自各个领域的专家的几个不同的哈希函数。功能是针对8位值设计的,但我相信您可以针对您的情况进行扩展。我不知道该建议什么,但我认为他们中的任何一个都应该比你现在的想法更好。
您提出的当前方法的问题是,值在字段2 ^ n中是循环的,如果您在结尾处使用mod 64,例如您丢失了大多数值,并且最终结果中只剩下最后3个值。
答案 1 :(得分:1)
尽管你持怀疑态度,我还是会通过一个标准的哈希函数来推动它们。 如果他们完全随机(并且相对独立 - 你不能说)从你开始,你可能不需要做太多的工作。在这种情况下,Fowler-Noll-Vo(FNV)是一个很好的候选人。
FNV采用一系列8位输入,输入为(逻辑上)4位。 我开始时甚至没有费心去打包两个两个&#39;如你所描述的那样 如果您想尝试这样做,只需逻辑填充奇数长度系列的消息长度(显然减少到4位值)。
我不希望打包来改善哈希值。它可以为您节省很少的周期,因为它会将相对昂贵的*
与<<
和|
进行交换。
尝试两者并报告回来!
以下是打包和&#39;普通&#39;的实现。 C中的FNV1a版本:
#include <inttypes.h>
static const uint32_t sFNVOffsetBasis=2166136261;
static const uint32_t sFNVPrime= 16777619;
const uint32_t FNV1aPacked4Bit(const uint8_t*const pBytes,const size_t pSize) {
uint32_t rHash=sFNVOffsetBasis;
for(size_t i=0;i<pSize;i+=2){
rHash=rHash^(pBytes[i]|(pBytes[i+1]<<4));
rHash=rHash*sFNVPrime;
}
if(pSize%2){//Length is odd. The loop missed the last element.
rHash=rHash^(pBytes[pSize-1]|((pSize&0x1E)<<3));
rHash=rHash*sFNVPrime;
}
return rHash;
}
const uint32_t FNV1a(const uint8_t*const pBytes,const size_t pSize) {
uint32_t rHash=sFNVOffsetBasis;
for(size_t i=0;i<pSize;++i){
rHash=(rHash^pBytes[i])*sFNVPrime;
}
return rHash;
}
注意:在添加长度时,我已编辑它以跳过第一位。显然奇数长度的底部位是100%偏向1.我不知道长度是如何分布的。把它放在开头而不是结束可能比较明智。