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

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

【问题讨论】:

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

标签: c++ unordered-map hash-function


【解决方案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++ 开源代码(包括GCCClang 的代码,它们都是C++ 编译器;或FLTKQt) 可在 githubgitlab 等网站上找到。您可能需要征得您的经理的许可才能研究此类代码。

      建议:阅读文档

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

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

      【讨论】:

        猜你喜欢
        • 2013-07-16
        • 2014-11-11
        • 2019-12-18
        • 1970-01-01
        • 1970-01-01
        • 2016-11-29
        • 1970-01-01
        • 1970-01-01
        • 2016-04-09
        相关资源
        最近更新 更多