当前位置: 首页 > 知识库问答 >
问题:

是否可以比使用hashmap更快地将字符串映射到int?

朱昊乾
2023-03-14

我知道我不应该优化我的程序的每一个点,所以请考虑这个问题是“学术性的”

我最大有100个字符串和整数,每个都是这样的:

MSFT 1
DELL 2
HP   4
....
ABC  58

这个集合是预先初始化的,这意味着一旦创建,它就永远不会更改。在集合初始化之后,我会非常密集地使用它,所以快速查找很好。字符串很短,最多30个字符。映射的int也有限,介于1和100之间。

至少知道字符串是预初始化的,并且永远不会更改,应该可以“找到”导致“一篮子一项”映射的哈希函数,但可能还有其他黑客攻击。

我可以想象一个优化——我只能读取第一个符号。例如,如果“DELL”是唯一以“D”开头的字符串,并且我收到了类似“D***”的消息,那么我甚至不需要读取该字符串!这显然是“戴尔”。这种查找必须比“hashmap查找”快得多。(在这里,我假设我们只接收散列中的符号,但情况并非总是如此)

对于我的问题,是否有任何现成的或易于实施的解决方案?我使用c和助推。

我检查了一下,发现我对股票代码的交换限制是12个符号,而不是上面提到的30个。然而,其他交易所可能允许稍微长的符号,所以让算法继续处理多达20个字符的长股票代码是很有趣的。

共有3个答案

施晗日
2023-03-14

哈希必须遍历字符串并生成哈希值。当使用链接[Wiki:trie]中解释的trie时,只需要遵循链接结构上的路径,而不需要任何过度计算。如果是压缩trie,如本页末尾所述,当首字母代表一个单词时,它会考虑计数一个案例(您提到的戴尔案例)。预处理稍高,但在运行时性能最佳。

还有一些优点:
1。如果您要查找的字符串不存在,您知道第一个字符与现有字符串不同(不需要继续计算)
2。实现后,直接向trie添加更多字符串。

闻人哲茂
2023-03-14

sehe帖子的小补充:

如果使用一个简单的std::map,净效果是前缀搜索(因为词典编纂的字符串比较快捷方式在第一个字符上不匹配)。对于排序容器中的二进制搜索也是如此。

您可以利用前缀搜索来提高效率。std::map和朴素二进制搜索的问题是,对于每个单独的比较,它们都会冗余地读取相同的前缀,使得整体搜索为O(m log n),其中m是搜索字符串的长度。

这就是为什么hashmap在大型集上胜过这两种方法的原因。然而

对于您的目的而言,trie或具有完美哈希的(直接查找)哈希表是否更有效是一个分析问题。

段超
2023-03-14

原则上,哈希表是最快的方式。

但是,鉴于您提前知道完整域的事实,您可以编译完美哈希函数。

有了完美的散列,就不需要冲突,所以可以将散列表存储在线性数组中!

通过适当的调整,你可以

  • 将所有哈希元素放在有限的空间中,使直接寻址成为一个潜在选项
  • 在O(1)中进行反向查找

用于生成完美哈希函数的“老式”工具是gperf(1)。维基百科列出了有关该主题的更多资源。

由于所有的争论,我运行了一个演示:

下载纳斯达克股票代码并从该集合中获取100个随机样本,应用gperf如下:

gperf -e ' \015' -L C++ -7 -C -E -k '*,1,$' -m 100 selection > perfhash.cpp

结果是哈希值MAX\u hash\u值为157,并且直接字符串查找表包含尽可能多的项。以下是用于演示的哈希函数:

inline unsigned int Perfect_Hash::hash (register const char *str, register unsigned int len) {
  static const unsigned char asso_values[] = {
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156,  64,  40,   1,  62,   1,
       41,  18,  47,   0,   1,  11,  10,  57,  21,   7,
       14,  13,  24,   3,  33,  89,  11,   0,  19,   5,
       12,   0, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
      156, 156, 156, 156, 156, 156, 156, 156, 156
    };
  register int hval = len;

  switch (hval) {
      default: hval += asso_values[(unsigned char)str[4]];   /*FALLTHROUGH*/
      case 4:  hval += asso_values[(unsigned char)str[3]];   /*FALLTHROUGH*/
      case 3:  hval += asso_values[(unsigned char)str[2]+1]; /*FALLTHROUGH*/
      case 2:  hval += asso_values[(unsigned char)str[1]];   /*FALLTHROUGH*/
      case 1:  hval += asso_values[(unsigned char)str[0]];   break;
  }
  return hval;
}

它真的没有变得更有效率。请查看github的完整源代码:https://gist.github.com/sehe/5433535

请注意,这也是一个完美的哈希,所以不会有碰撞

Q、 […]这显然是“戴尔”。这种查找必须比“hashmap查找”快得多。

A: 如果使用一个简单的std::map,净效果是前缀搜索(因为词典编纂的字符串比较快捷方式在第一个字符上不匹配)。对于排序容器中的二进制搜索也是如此。

[1]PS。对于100个字符串,由于改进了引用位置,使用std::searchstd::lower_bound的排序字符串数组可能会同样快/更快。请查阅您的配置文件结果,看看这是否适用。

 类似资料:
  • 问题内容: 我正在制作一个程序,要求至少每秒捕获24个屏幕截图。目前,使用下面的代码,我每94毫秒仅获得1个,因此大约为10毫秒。 我不想使用任何第三方库,因为我试图将其保持尽可能小,但是如果我希望获得显着的性能提升,我会愿意的。我也试图保持该平台独立,但是,如果确实能够显着提高性能,我愿意将其限于Windows。 编辑:我现在也尝试了两种不同的方法;使用在oracles网站上找到的代码段,并在下

  • 嗨,我尝试将以下Source类映射到以下Destation类。我使用了以下映射以将字符串值映射到列表字符串。它没有正确映射。我需要知道如何使用Dozer将2个字符串值映射到一个目标字符串列表中。

  • 我有一个类,我们用一个映射字段将其称为a,它被转换为B类,用于数据库存储/检索,其中该字段映射到字符串。从A到B的映射非常有效。然而,当从B到A时,我得到了一个IllegalArgument异常,它表示无法将字符串转换为映射。让我困惑的是,Dozer的文档中说,这确实适用于以下情况: 数据类型转换由Dozer映射引擎自动执行。目前,Dozer支持以下类型的转换:(这些都是双向的) 然后它继续列出要

  • 问题内容: 说我有下表: 是否可以使用递归CTE生成以下输出: 我已经试了一下,但是还没能使它正常工作。我会使用其他技术做得更好吗? 问题答案: 我不建议这样做,但是我设法解决了。 桌子: 数据: 询问: 根据要求,这是XML方法:

  • 我有下面的结构,我想用MapStruct映射这个。 下面是mapstruct为toDTO方法生成的实现 下面是mapstruct为toEntity方法生成的实现 我的问题是方法只在文本不为空时设置注释。但是方法不检查空文本或空文本。因此,如果我在DTO中获得,它将创建一个新的comment对象并将文本设置为null。如何避免这一点?有人能解释一下这种行为并建议我正确的做法吗?谢了!

  • 我是Mapstruct的新手。我有一个Word对象,它包含一个字符串值和一组它自己,我想把它映射到WordDTO,它包含一个值和一组字符串值。我不知道怎么做。正如我在注释中所说,mapstruct不能映射两个对象是有道理的,但如果它有帮助,我将错误放在下面: 我为映射实现了这个接口: 谢谢你的帮助。