从2+个整数创建哈希/种子值(快速)

时间:2015-05-04 14:39:37

标签: c++ random hash

我正致力于生成不同类型的渐变噪音。这种噪声所需的一个因素是给定位置向量的随机向量的生成。

此位置矢量可以是单个int,2D位置,3D位置,4D位置等任何位置。

除此之外,还有一个额外的种子"价值是必要的。

需要的是将这些n + 1个整数的哈希值转换为一个唯一的整数,我可以用它来为PRNG提供种子。这些值非常重要,因为每次使用相同的值时我都需要能够检索原始种子。

到目前为止,我已尝试过Fowler-Noll-Vo的实施;但这对我的目的来说太慢了。

我也试过使用连续调用配对功能:

int pairing_function(int x, int y)
{
    return(0.5*(x+y)*(x+y+1) + x);
}

即:

int hash = pairing_function(pairing_function(x,y),seed);

但似乎发生的事情是,如果种子数量足够大,则值会超出int的大小(甚至更大的类型)。

实现我在这里尝试做的事情的好方法是什么?重要的是速度超过任何加密问题,以及不返回大于原始数据类型的数字。

我使用的是C ++,但只要任何代码都可读,我就可以把它搞定。

3 个答案:

答案 0 :(得分:1)

听起来你使用的FNV可能由于其使用方式而效率低下。这里(我认为,我还没有测试过)同样的事情,可以简单地内联。

inline uint32_t hash(uint32_t h, uint32_t x) {
  for (int i = 0; i < 4; i++) {
    h ^= x & 255;
    x >>= 8;
    h = (h << 24) + h * 0x193;
  }
  return h;
}

我认为调用hash(hash(2166136261, seed), x)hash(hash(hash(2166136261, seed), x), y)会给你相同的结果(假设是little-endian)作为库函数。

但是,为了提高散列质量的成本,您可以尝试这样的更改:

inline uint32_t hash(uint32_t h, uint32_t x) {
  for (int i = 0; i < 2; i++) {
    h ^= x & 65535;
    x >>= 16;
    h = (h << 24) + h * 0x193;
  }
  return h;
}

甚至:

inline uint32_t hash(uint32_t h, uint32_t x) {
  h ^= x;
  h = (h << 24) + h * 0x193;
  return h;
}

这些变化会稍微削弱低阶位,因此您希望遵循标准惯例优先使用高阶位。例如,如果您只需要16位,那么将最终结果右移16,而不是用0xffff掩盖它;

h = ...行会定期溢出一个int,但它依赖于标准的mod-2 ** 32行为。如果这是一个问题,那么您将要用不同的东西替换该行,并且可能在您的散列中接受更少的有用位。也许是h = (h >> 4) + (h & 0x7fffff) * 0x193;,但这只是一个随机的调整,我还没有检查它的哈希质量。

答案 1 :(得分:0)

奇怪的是,FNV 方式太慢因为每个字节数据只有1 xor和1个整数乘积。来自Wikipedia [it is] 旨在快速计算

如果你想要一些非常快的东西,你可以试试这些实现,其中乘法被编码为移位和加法:

丹·伯恩斯坦执行:

unsigned long
hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

sdbm实现(hash(i)= hash(i - 1)* 65599 + str [i]):

static unsigned long
sdbm(str)
unsigned char *str;
{
    unsigned long hash = 0;
    int c;

    while (c = *str++)
        hash = c + (hash << 6) + (hash << 16) - hash;

    return hash;
}

参考"Hash Functions" from cse.yorku.ca

答案 2 :(得分:0)

我会在

上挑战你
  

到目前为止,我已尝试过Fowler-Noll-Vo的实施;但这对我的目的来说太慢了。

在一些简单的基准测试中,我已经完成了FNV哈希是最快的。我假设你已经尝试了所有哈希的基准测试?

对于基准测试,我只是简单地测量了MVSC ++ 2013中使用两个32位unsigned int输入的10亿个哈希值的各种算法所花费的时间:

  • FNV(32位) = 222M哈希/秒
  • 您的pairing_function() = 175M哈希/秒
  • 简单哈希x + (y << 10) = 170M哈希/秒
  • hash() pairing_function()函数使用int pairing_function(int x, int y) { return((x+y)*(x+y+1)/2 + x); } = 167M哈希/秒
  • Dan Bernstein = 101M哈希/秒

显然这些是非常基本的基准测试结果,我不一定非常信任他们。看到一些算法在不同平台和编译器上运行得更快/更慢,我不会感到惊讶。

总的来说,尽管FNV在这种情况下是最快的,但在最快和最慢之间只有两个差异因素。如果这确实对你的情况有所影响,我建议你再看一下你的问题,看它是否可以重新设计,不需要哈希,或者至少减少对哈希速度的依赖。

注意:我将您的配对功能更改为:

double

对于上述基准。使用您的版本会导致转换为hash(),导致x5变慢,#define FNV_32_PRIME 16777619u unsigned int FNVHash32(const int input1, const int input2) { unsigned int hash = 2166136261u; const unsigned char* pBuf = (unsigned char *) &input1; for (int i = 0; i < 4; ++i) { hash *= FNV_32_PRIME; hash ^= *pBuf++; } pBuf = (unsigned char *) &input2; for (int i = 0; i < 4; ++i) { hash *= FNV_32_PRIME; hash ^= *pBuf++; } return hash; } 函数x8变慢。

<强>更新

对于FNV哈希,我找到了一个source online并从那里修改它直接在2个整数上工作(假设一个32位整数):

<input type="file" webkitdirectory="webkitdirectory" directory="directory" multiple="multiple"/>

由于FNV仅适用于字节,因此您可以将其扩展为使用任意数量的整数或其他数据。