C++ 预先知道的字符串的完美哈希函数

C++ 预先知道的字符串的完美哈希函数,c++,string,hash,perfect-hash,C++,String,Hash,Perfect Hash,我有4000个字符串,我想用这些字符串创建一个完美的哈希表。字符串是预先知道的,所以我的第一个想法是使用一系列if语句: if (name=="aaa") return 1; else if (name=="bbb") return 2; . . . // 4000th `if' statement 然而,这将是非常低效的。有更好的方法吗?是一种能够做到这一点的工具: GNUgperf是一个完美的哈希函数生成器。对于

我有4000个字符串,我想用这些字符串创建一个完美的哈希表。字符串是预先知道的,所以我的第一个想法是使用一系列
if
语句:

 if (name=="aaa")
      return 1;
 else if (name=="bbb")
      return 2;
        .
        .
        .
 // 4000th `if' statement
然而,这将是非常低效的。有更好的方法吗?

是一种能够做到这一点的工具:

GNU
gperf
是一个完美的哈希函数生成器。对于给定的字符串列表,它以C或C++代码的形式生成哈希函数和哈希表,用于根据输入字符串查找值。哈希函数是完美的,这意味着哈希表没有冲突,哈希表查找只需要一个字符串比较

根据文献,使用代码> GPFF < /C> >生成GNU C、GNU C++、GNU java、GNU PASCAL、GNU MULTRA 3和GNU缩进中的词汇表保留关键字识别器。p>


Douglas C.Schmidt在中描述了它的工作方式。

我相信@NPE的答案是非常合理的,我怀疑它对于您的应用程序来说可能太多了

考虑以下示例:假设您的“引擎”逻辑(即:应用程序的功能)包含在名为
engine.hpp的文件中:

// this is engine.hpp
#pragma once
#include <iostream>
void standalone() {
  std::cout << "called standalone" << std::endl;
}
struct Foo {
  static void first() {
    std::cout << "called Foo::first()" << std::endl;
  }
  static void second() {
    std::cout << "called Foo::second()" << std::endl;
  }  
};
// other functions...
您可以使用以下gperf输入文件(我称之为“lookups.gperf”)来实现这一点:

然后,您可以使用gperf通过一个简单的命令创建
lookups.hpp
文件:

 gperf -tCG lookups.gperf > lookups.hpp
一旦设置好,以下
main
子例程将根据我键入的内容分派命令:

#include <iostream>
#include "engine.hpp" // this is my application engine
#include "lookups.hpp" // this is gperf's output

int main() {

  std::string command;

  while(std::cin >> command) {
    auto match = Commands::Lookup(command.c_str(), command.size());
    if(match) {
      match->dispatch();
    } else {
      std::cerr << "invalid command" << std::endl;
    }
  }
}
然后运行它:

$ ./a.out
standalone
called standalone
first
called Foo::first()
Second
called Foo::second()
SECOND
called Foo::second()
first
called Foo::first()
frst
invalid command
请注意,一旦生成了
lookups.hpp
,您的应用程序在gperf中就没有任何依赖关系


免责声明:这个例子的灵感来源于。

由于这个问题仍然没有答案,而且我将向我的HFT平台添加相同的功能,我将分享我的清单,了解C++中的完美哈希算法。要找到一个开放的、灵活的、无bug的实现比我想象的要困难,所以我要分享我还没有放弃的实现:

  • CMPH图书馆,包括论文集和此类算法--
  • BBHash,论文作者的又一个实现--
  • Ademakov的——上述论文的另一个实现--
  • WHHNE/PHF-我现在正在检查这一个,并试图解决在处理大型密钥集时C++字符串的一些分配错误——
  • EMPFF——似乎未加控制--

迟做总比不做好,我相信这终于回答了OP的问题:

简单使用--C++的不可变容器的编译时(COSTEXPR)库(使用引擎盖下的“完美散列”)。< /P> 在我的测试中,它与著名的GNU的gperfperfect哈希C代码生成器一起执行

关于伪代码术语:

#include <frozen/unordered_map.h>
#include <frozen/string.h>

constexpr frozen::unordered_map<frozen::string, int, 2> olaf = {
    {"aaa", 1},
    {"bbb", 2},
    .
    .
    .
    // 4000th element
};

return olaf.at(name);
#包括
#包括
constexpr冻结::无序映射olaf={
{“aaa”,1},
{“bbb”,2},
.
.
.
//第4000元素
};
返回olaf.at(名称);
将在O(1)时间内响应,而不是OP的O(n)
--O(n)假设编译器不会优化你的if链,它可能会这样做)

如果你事先知道所有的字符串,你确定需要对它们进行散列吗?我需要将一个图存储为邻接列表,所以我想需要散列表。@NPEHashing是对这么多字符串进行此操作的最有效方法,如果你有关于字符串的先验信息,你可以使用gperf之类的东西来创建一个好的哈希函数。我认为这对我的应用程序来说太难了,我更喜欢简单的东西。我将把这个线程标记为书签,以查看比gperf更简单的完美解决方案。运行时解决方案可能更容易构建。此库显然在内存方面更高效:
 g++ main.cpp -std=c++11
$ ./a.out
standalone
called standalone
first
called Foo::first()
Second
called Foo::second()
SECOND
called Foo::second()
first
called Foo::first()
frst
invalid command
#include <frozen/unordered_map.h>
#include <frozen/string.h>

constexpr frozen::unordered_map<frozen::string, int, 2> olaf = {
    {"aaa", 1},
    {"bbb", 2},
    .
    .
    .
    // 4000th element
};

return olaf.at(name);