我们如何为 C++ 无序集定制我们自己的哈希函数以获得特定的顺序?

Posted

技术标签:

【中文标题】我们如何为 C++ 无序集定制我们自己的哈希函数以获得特定的顺序?【英文标题】:How can we customize our own hash function for C++ unordered set to gain a specific order? 【发布时间】:2020-07-22 16:01:36 【问题描述】:

在竞争性编码中,我们面临许多问题,我们必须按输入顺序提供输出。所以,我们需要制作自己的哈希函数。知道如何编写自己的哈希函数吗?

【问题讨论】:

您想订购一个无序的集合或映射吗? 如果您能找到一个具有 1 对 1 映射的哈希函数,那么您也可以找到它的逆函数,这将为您提供原始排序。 【参考方案1】:

...你不能。 unordered_set无序的。编写自己的哈希函数不会改变这一点。一个特定的标准库实现可以为一个特定的散列函数和存储在unordered_set 中的一组特定数据持有一个订单。但是这样的代码不仅不能移植,而且可以通过向该集合添加更多内容来更改顺序。

如果您需要按照输入的顺序提供一些输出,那么您应该使用一个容器来保留您给它的顺序,例如vector

【讨论】:

【参考方案2】:

由于无序容器的实现使用哈希表(标准几乎要求它们)和链表的单独链接,如果你确保桶的数量超过你的哈希函数的范围(使用reserve())那么很有可能 - 尽管不能保证 - 元素将按其哈希值的顺序存储,并且对于具有相同哈希值的元素按插入顺序存储。

我重申,这不能保证,但在一个知道你实现的编码竞赛中你可能会侥幸逃脱。

此外,这当然是低效的,因为您要么需要保留大量存储桶,需要大量内存使用,要么限制哈希函数的范围,从而导致冲突。使用有序容器会更好。

【讨论】:

【参考方案3】:

我们如何为 C++ 无序集定制自己的哈希函数以获得特定的顺序?

你不能可靠地做你想做的事。

但是你为什么不使用std::set(或std::map)来达到这个目的呢?查看this C++ reference,并阅读好的C++ programming book(和C++11 标准n3337),了解更多信息。

我们不知道您的实际用例是什么,但我可能建议您创建自己的 class,遵循 C++ rule of five,并拥有两者 std::mapstd::hash_map 表示相同数学关系。

 class YourClass 
    // incomplete, should follow the rule of five
 private:
    std::map<std::string, long> mapstr;
    std::unordered_map<std::string, long> hashstr;
 public:
    void put(const std::string&str, long n)  
         mapstr.insert(str,n);
         hashstr.insert(str,n);
    
 /// etc...
 ; 

当然,如果您正在编写多线程程序,则需要在上面的类中有一些 std::mutex 字段,以使用 std::lock_guard 序列化访问......

知道如何编写自己的哈希函数吗?

编写一个足够好的哈希函数通常很容易。

编写一个非常有效的哈希函数仍然可以让你获得博士学位,并且你会在ACM 赞助的会议上找到许多关于该主题的论文。

这是一个 simplenaive 对字符串的哈希函数:

std::size_t naive_string_hash(const std::string&str) 
   constexpr unsigned k1 = 78139; // a prime number
   constexpr unsigned k2 = 98129; // another prime number
   std::size_t h = 38197; // yet another prime number
   for (char c: str) 
     h = (k1 * h) ^ (k2 * (unsigned)c);
   return h;

您可以用+ 替换按位独占或^ 并阅读Bézout's identity。

推荐:研究现有 open source代码

我强烈建议寻找现有 C++ 开源代码(包括GCC 和Clang 的代码,它们都是C++ 编译器;或FLTK 或Qt) 可在 github 或 gitlab 等网站上找到。您可能需要征得您的经理的许可才能研究此类代码。

建议:阅读文档

我邀请您阅读您的 C++ 编译器(可能是 GCC 或 Clang)、您的链接器(可能是 binutils)、您的 source code editor(我喜欢 GNU emacs)、您的文档版本控制系统(例如git)。如果允许的话,我建议在你的电脑上使用 GNU/Linux 系统(例如Debian 或Ubuntu)(因为 Linux 主要是由开源组件组成的,其源代码你可以下载学习)。

另见http://linuxfromscratch.org/和https://norvig.com/21-days.html

【讨论】:

以上是关于我们如何为 C++ 无序集定制我们自己的哈希函数以获得特定的顺序?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这些 C++ STL 无序集不被视为相等?

如何为Serializer分页查询集

C++ 哈希表查询_进入哈希函数结界的世界

如何为 .NET 应用程序域重新加载程序集?

如何为箭头包安装 C++ 依赖项?

在定义自定义选项组件时,如何为react-select应用默认样式?