c++ - 将多个非数字插入 std::unordered_set
问题描述
std::unordered_set<double>
IEEE 754 标准的后果之一是在插入非数字元素时的非直观行为NAN
。
由于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
(现场观看)中有两个元素:NAN
和NAN
!
我的主要问题是,当将N
NAN
s 插入哈希集中时,它们都命中了相同的哈希桶,并且N
插入哈希集中的性能退化到最坏情况下的运行时间 - O(N^2)
。
例如,请参阅问题末尾的列表或此处实时- 插入NAN
比“正常”浮点数花费更多时间。
我的问题:是否有可能(如果是的话 - 如何)以std::unordered_set<double>
这样的方式进行调整,即集合中最多有一个-NAN
元素,无论插入NAN
的 s (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
}
解决方案
您可以定义自己的谓词来比较键,并为此目的确保 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。
推荐阅读
- c - 使用定义的运算符时,双嵌套括号会在宏内部引发错误
- python - HTML 表格中不显示“价格”和“总计”(CS50 - 财务)
- javascript - Vue - chrome.webstore.install “无法读取未定义的属性‘安装’”
- python - 基本命令突然不起作用!不和谐.py
- tailwind-css - 顺风问题 - 一些项目略有偏差
- javascript - javascript streamreader 仅在#log-box.append 中显示第二个块
- ssis - SSIS 包 - 如何使用 foreach 循环容器生成多个文件?
- api - API paypal 我可以使用帐户标准吗?
- javascript - React Redux ->如何映射我的状态?
- android - 代码的位置,以便“SharedPreferences”与“onOptionsItemSelected”一起使用