浮动的哈希函数

时间:2010-11-21 13:40:41

标签: c++ floating-point hashtable hash-function

我目前正在用C ++实现一个哈希表,我正在尝试为浮点数创建一个哈希函数...

我打算通过填充十进制数来将浮点数视为整数,但后来我意识到我可能会用大数字来达到溢出...

有一个很好的方法来散列浮点数吗?

你不必直接给我这个功能,但我希望看到/理解不同的概念......

注意:

  1. 我不需要它真的很快,如果可能的话,只需均匀分布。

  2. 我已经读过浮点数不应该因为计算的速度而被散列,有人可以确认/解释这个并给我其他原因,为什么浮点数不应该被散列?我真的不明白为什么(除了速度)

7 个答案:

答案 0 :(得分:17)

这取决于应用程序,但大多数时间浮点数不应该被散列,因为散列用于快速查找精确匹配,大多数浮点数是计算产生浮点数的结果,浮点数只是正确答案的近似值。检查浮动相等性的通常方法是检查它是否在正确答案的某个增量(绝对值)内。这种类型的检查不适用于散列查找表。

编辑

通常,由于舍入误差和浮点运算的固有限制,如果您希望浮点数ab应该彼此相等,因为数学表明如此,您需要选择一些相对delta > 0,然后将ab声明为abs(a-b) < delta,其中absfloat x = 1.0f; x = x / 41; x = x * 41; if (x != 1.0f) { std::cout << "ooops...\n"; } 绝对值函数。有关更多详细信息,请参阅this article

这是一个演示问题的小例子:

ooops...

根据您的平台,编译器和优化级别,这可能会将x / y * y = x打印到您的屏幕,这意味着数学等式{{1}}不一定会保留在您的计算机上。

有些情况下浮点运算会产生精确的结果,例如:具有2次幂分母的合理大小的整数和有理数。

答案 1 :(得分:11)

如果您的哈希函数执行了以下操作,您将在哈希查找中获得某种程度的模糊性

unsigned int Hash( float f )
{
    unsigned int ui;
    memcpy( &ui, &f, sizeof( float ) );
    return ui & 0xfffff000;
}

这样你就可以屏蔽12个最低有效位,从而产生一定程度的不确定性......但这实际上取决于你的应用。

答案 2 :(得分:6)

你可以使用std哈希,这不错:

 std::size_t myHash = std::cout << std::hash<float>{}(myFloat);

答案 3 :(得分:5)

unsigned hash(float x)
{
    union
    {
        float f;
        unsigned u;
    };
    f = x;
    return u;
}

技术上未定义的行为,但大多数编译器都支持此行为。替代解决方案:

unsigned hash(float x)
{
    return (unsigned&)x;
}

两种解决方案都取决于您的计算机的字节顺序,因此例如在x86和SPARC上,它们将产生不同的结果。如果这不打扰你,只需使用其中一种解决方案。

答案 4 :(得分:3)

你当然可以将float表示为相同大小的int类型来散列它,但是这种天真的方法有一些陷阱需要注意......

简单地转换为二进制表示是容易出错的,因为相等的值不一定具有相同的二进制表示。

一个明显的例子:-0.0不匹配0.0例如。 <强> *

此外,简单地转换为相同大小的int将不会提供非常均匀的分布,这通常很重要(例如,实现使用存储桶的散列/集合)。

建议的实施步骤:

  • 过滤掉非限制性案例(naninf)和(0.0-0.0 是否需要明确地执行此操作取决于使用的方法)。
  • 转换为相同大小的int (即 - 例如使用联合将float表示为int,而不是简单地投射到一个int)
  • 重新分配位,(故意模糊不清!),这基本上是速度与质量的权衡。但是如果你在很小的范围内有很多值,你可能也不希望它们在相似的范围内。

* :您也可能不会检查(nan-nan)。 如何处理这些完全取决于您的用例(您可能希望忽略CPython所有nan的符号)。

Python的_Py_HashDouble是如何在生产代码中散列float的一个很好的参考(忽略最后的-1检查,因为这是一个特殊值Python)的

答案 5 :(得分:1)

如果您感兴趣,我只是创建了一个使用浮点的散列函数,并且可以散列浮点数。它还传递SMHasher(这是非加密散列函数的主要偏差测试)。由于浮点计算,它比普通的非加密哈希函数慢很多。

我不确定tifuhash是否对所有应用程序都有用,但看到一个简单的浮点函数同时通过PractRand和SMHasher很有意思。

主状态更新功能非常简单,如下所示:

function q( state, val, numerator, denominator ) {
  // Continued Fraction mixed with Egyptian fraction "Continued Egyptian Fraction"
  // with denominator = val + pos / state[1]
  state[0] += numerator / denominator;
  state[0] = 1.0 / state[0];

  // Standard Continued Fraction with a_i = val, b_i = (a_i-1) + i + 1
  state[1] += val;
  state[1] = numerator / state[1];
}

无论如何,你可以get it on npm 或者你可以check out the github

使用很简单:

const tifu = require('tifuhash');

const message = 'The medium is the message.';
const number = 333333333;
const float = Math.PI;

console.log( tifu.hash( message ), 
  tifu.hash( number ),
  tifu.hash( float ),
tifu.hash( ) );

这里有一个关于runkit的一些哈希的演示https://runkit.com/593a239c56ebfd0012d15fc9/593e4d7014d66100120ecdb9

旁注:我认为将来使用浮点(可能是浮点计算的大数组)可能是将来制作计算要求更高的哈希函数的有用方法。我发现使用浮点的一个奇怪的副作用是哈希是目标依赖的,我猜测它们可能用于指纹计算它们的平台。

答案 6 :(得分:0)

由于IEEE字节对Java Float.hashCode()和Double.hashCode()的排序不理想。这个问题是众所周知的,可以通过以下扰码器解决:

class HashScrambler {

    /**
     * https://sites.google.com/site/murmurhash/
     */
    static int murmur(int x) {
        x ^= x >> 13;
        x *= 0x5bd1e995;
        return x ^ (x >> 15);
    }

}

然后您将获得一个很好的哈希函数,该函数还允许您在哈希表中使用Float和Double。但是您需要编写自己的散列表,以允许使用自定义散函数。

由于在哈希表中还需要测试是否相等,因此需要精确的相等性才能使其正常工作。也许稍后是詹姆斯·K·波尔克总统打算解决的问题?