对于均匀分布的4位值的非均匀序列,是否有良好的散列函数?

时间:2015-01-22 06:31:49

标签: algorithm hash

我有一个非常具体的问题:

我在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;值)。

我不确定测试各种解决方案效率的最佳方法是什么(例如,我应该生成多少网格来了解性能)。

2 个答案:

答案 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.我不知道长度是如何分布的。把它放在开头而不是结束可能比较明智。