我目前正在用C ++实现一个哈希表,我正在尝试为浮点数创建一个哈希函数...
我打算通过填充十进制数来将浮点数视为整数,但后来我意识到我可能会用大数字来达到溢出...
有一个很好的方法来散列浮点数吗?
你不必直接给我这个功能,但我希望看到/理解不同的概念......
注意:
我不需要它真的很快,如果可能的话,只需均匀分布。
我已经读过浮点数不应该因为计算的速度而被散列,有人可以确认/解释这个并给我其他原因,为什么浮点数不应该被散列?我真的不明白为什么(除了速度)
答案 0 :(得分:17)
这取决于应用程序,但大多数时间浮点数不应该被散列,因为散列用于快速查找精确匹配,大多数浮点数是计算产生浮点数的结果,浮点数只是正确答案的近似值。检查浮动相等性的通常方法是检查它是否在正确答案的某个增量(绝对值)内。这种类型的检查不适用于散列查找表。
编辑:
通常,由于舍入误差和浮点运算的固有限制,如果您希望浮点数a
和b
应该彼此相等,因为数学表明如此,您需要选择一些相对小delta > 0
,然后将a
和b
声明为abs(a-b) < delta
,其中abs
是float 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
将不会提供非常均匀的分布,这通常很重要(例如,实现使用存储桶的散列/集合)。
建议的实施步骤:
nan
,inf
)和(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·波尔克总统打算解决的问题?