当插入非数字元素(std::unordered_set<double>
)时,NAN
的非直观行为是IEEE 754标准的后果之一。
由于NAN!=NAN
遵循以下顺序:
#include <iostream>
#include <cmath>
#include <unordered_set>
int main(){
std::unordered_set<double> set;
set.insert(NAN);
set.insert(NAN);
std::cout<<"Number of elements "<<set.size()<<"\n"; //there are 2 elements!
}
set
(see it live)中有两个元素:NAN
和NAN
!
与我有关的主要问题是,将N
NAN
s插入哈希集中时,它们都命中了相同的哈希值,并且N
的性能插入哈希集会退化为最坏的运行时间-O(N^2)
。
例如,请参阅问题末尾的清单或here live-插入NAN
所花的时间要比“普通”浮点数多几个数量级。
我的问题:是否可以(如果是-如何)以这样一种方式来调整std::unordered_set<double>
,以使集合中最多包含一个NAN
元素,而无论插入了NAN
个(NAN
,-NAN
等)?
列表:
#include <iostream>
#include <cmath>
#include <unordered_set>
#include <chrono>
constexpr int N=5000;
void test_insert(double value)
{
std::unordered_set<double> s;
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
s.insert(value);
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Duration: " << (std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() / 1e9) << "\n";
std::cout << "Number of elements: "<<s.size()<<"\n";
}
int main(){
std::cout << "Not NAN\n";
test_insert(1.0); //takes 0.0001 s
std::cout << "NAN\n";
test_insert(NAN); //takes 0.2 s
}
答案 0 :(得分:7)
您可以定义自己的谓词以比较键,并为此确保NaN比较相等。可以将其作为std::unordered_set
类模板的第三个参数。
请参见下面的EqualPred
定义(从问题中复制并修改的代码),及其在声明unordered_set
变量中的使用。我在https://en.cppreference.com/w/cpp/container/unordered_set
实时演示:http://coliru.stacked-crooked.com/a/7085936431e6698f
#include <iostream>
#include <cmath>
#include <unordered_set>
#include <chrono>
struct EqualPred
{
constexpr bool operator()(const double& lhs, const double& rhs) const
{
if (std::isnan(lhs) && std::isnan(rhs)) return true;
return lhs == rhs;
}
};
constexpr int N=5000;
void test_insert(double value)
{
std::unordered_set<double, std::hash<double>, EqualPred> s;
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
s.insert(value);
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Duration: " << (std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() / 1e9) << "\n";
std::cout << "Number of elements: "<<s.size()<<"\n";
}
int main(){
std::cout << "Not NAN\n";
test_insert(1.0); //takes 0.0001 s
std::cout << "NAN\n";
test_insert(NAN); //takes 0.2 s
}
值得注意的是(由于@ead的评论)-NaN
和+NaN
可能会散列为不同的值。如果要以相同的方式处理它们,则需要提供第二个模板参数(哈希函数)的不同实现。这应该检测到任何NaN并每次都对相同的NaN进行哈希处理。
答案 1 :(得分:3)
从您对安德鲁斯答案的评论中,
我认为此解决方案存在问题:-NAN将具有与NAN不同的哈希值,但对于哈希函数h必须成立:如果x == y,则h(x)== h(y)>
这对散列的处理方式有所不同,因此,如果需要"`column_name`"
...
(来自@Andrew回答)
h(-NAN) == h(NAN)