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

时间:2014-12-29 18:36:27

标签: c++ string hash perfect-hash

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

 if (name=="aaa")
      return 1;
 else if (name=="bbb")
      return 2;
        .
        .
        .
 // 4000th `if' statement

然而,这将是非常低效的。还有更好的方法吗?

4 个答案:

答案 0 :(得分:6)

gperf是一个完全相同的工具:

  

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

根据文档,gperf用于为GNU C,GNU C ++,GNU Java,GNU Pascal,GNU Modula 3和GNU缩进中的词法分析器生成保留关键字识别器。

道格拉斯·施密特在GPERF: A Perfect Hash Function Generator 中描述了它的运作方式。

答案 1 :(得分:2)

迟到总比没有好,我相信这现在终于回答了 OP 问题:

只需使用 https://github.com/serge-sans-paille/frozen -- 一个 C++ 不可变容器的编译时 (constexpr) 库(在幕后使用“完美散列”)。

在我的测试中,它与著名的 GNU 的 gperf 完美哈希 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);

将在 O(1) 时间内做出响应,而不是 OP 的 O(n) -- O(n) 假设编译器不会优化你的 if 链,它可能会这样做)

答案 2 :(得分:1)

我相信@ 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...

并假设你想根据地图调度不同的功能:

"standalone" dispatches void standalone()
"first" dispatches Foo::first()
"second" dispatches Foo::second()
# other dispatch rules...

您可以使用以下gperf输入文件(我称之为“lookups.gperf”)来执行此操作:

%{

#include "engine.hpp"

struct CommandMap {
    const char *name;
    void (*dispatch) (void);
};

%}

%ignore-case
%language=C++
%define class-name Commands
%define lookup-function-name Lookup
struct CommandMap

%%
standalone, standalone
first, Foo::first
second, Foo::second

然后,您可以使用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;
    }
  }
}

编译:

 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

请注意,一旦生成lookups.hpp,您的应用程序就无法在gperf中进行任何依赖。

免责声明:我从this site获取了此示例的灵感。

答案 3 :(得分:1)

由于问题仍未得到解答,并且我将向HFT平台添加相同的功能,因此我将共享 C ++中的完美哈希算法的清单。我想找到一个开放,灵活且无错误的实施方式更加困难,所以我要分享那些我尚未放弃的实施方式: