字符串上的唯一整数/长哈希密钥生成,以便更快地进行比

时间:2009-07-02 16:06:43

标签: algorithm string hash comparison

我很好奇其他人是如何解决这个问题的,以及天真解决方案背后可能存在的问题:

我有一个处理股市数据的系统。成千上万的符号,以及相关的价格/大小,以每毫秒几千的速率流入系统。

每个tick上需要进行的基本操作之一是字符串比较,以查看传入是否与我们感兴趣的符号匹配。在如此高的频率下,优化这些字符串比较可以在性能上产生可测量的差异。整个系统。

我正在考虑生成符号字符串的哈希值,并将其与记录一起存储。对于后续比较,系统应使用此哈希(为int或long,比较应该是单个操作,而不是遍历字符串的每个字符,直到找到不匹配)。

让我们忽略生成散列本身的成本(实际上,这可能实际上是令人望而却步的)。我能看到的唯一问题是,对于大量唯一符号,哈希冲突(两个单独的符号生成相同的哈希)将是毁灭性的。是否有散列算法可以保证匹配某些约束的字符串(例如字符数限制)是唯一的?

编辑:我将用Java编写此代码。不确定hashCode的(碰撞)质量或计算速度。

12 个答案:

答案 0 :(得分:12)

也许哈希函数不是最好的方法。如果您收到一个股票代码(而不是股票代码的散列),您将不得不每次计算它的哈希值。如果它的哈希算法没有冲突,那么无论如何你都需要查看符号的每个字符。所以你不妨直接比较一下这些人物。

我建议建立一个你感兴趣的所有代码的Trie数据结构。(见http://en.wikipedia.org/wiki/Trie)。遍历每个符号的树,如果到达股票代码的末尾而没有找到匹配,那么它不是一个有趣的股票代码。

使用散列,无论如何,你都必须在有趣代码的所有散列值集合中进行遍历。

答案 1 :(得分:5)

常见的加密散列函数(如SHA-1)输出20个字节(160位)。你的股票代码有多长?如果我们谈论ticker symbols如“WMT”(沃尔玛),“KO”(可口可乐)等,那么它们似乎只有几个字节长 - 因此比较应该更快它们直接代替处理20字节的哈希值。你提到哈希冲突 - 我不担心它们,尤其是当输入远小于哈希输出时。

您可以根据编程语言和平台将字节转换为intlong,然后在一条CPU指令中对这些“数字”进行比较。 (我不知道现代编译器是否可以通过调用memcmp来快速比较一堆字节?)

答案 2 :(得分:4)

您应该考虑使用Perfect hash function,我认为它符合您的要求

答案 3 :(得分:2)

如果您使用String.intern()或您自己的字符串池,那么您可以使用==而不是.equals() - 我已经在类似的性能关键代码中完成了这一点,它已经产生了很大的不同。默认String已经有一个hashCode(),它可以非常有效地工作。

我刚刚意识到这不是一个Java问题,但同样适用。是的,哈希然后使用身份检查可以节省时间。 java哈希算法使用:

     s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1]
 

答案 4 :(得分:2)

如果您收到4个字母的股票代码,那么每个字母应该可以表示为单个字节。将所有4个一起打包成32位int,瞧,你有“哈希”。现在,您可以使用单个机器指令将其与参考进行比较。

如果你没有使用Java,那就是。

我真的不建议将Java用于速度至关重要的任何事情,特别是每毫秒不要进行数千次字符串比较。

编辑:如果你想使用64位代码,你可以为每个long int打包最多8个字母,然后在1个指令中进行比较。

答案 5 :(得分:1)

您可以通过将字符串视为Base-27数字来生成哈希值(假设符号仅包含字母)。这将产生您正在寻找的唯一性。例如:

  

(无字母)= 0,A = 1,B = 2,...... Z = 26

     

AA =(1 x 27 1 )+(1 x 27 0 )= 28

     

AAA =(1 x 27 2 )+(1 x   27 1 )+(1 x 27 0 )= 757

     

BBB =(2 x 27 2 )+(2 x   27 1 )+(2 x 27 0 )= 1514

     <=> GOOG =(7 x 27 3 )+(15 x 27 2 )   +(15 x 27 1 )+(7 x 27 0 )= 149128

在32位int中最多可以使用6个字符。

答案 6 :(得分:1)

你想要的是具有良好辨别能力的快速哈希函数。 对于每个字符串,计算关联的散列函数并将其与字符串一起存储。 然后进行比较,代码:      if(哈希(s1)==哈希(s2)           &安培;&安培; S1 == S2)      然后 { ... } 除非哈希匹配,否则不会发生实际的字符串比较 仅在字符串匹配时才会出现。

有些人会告诉你实现一个完美的哈希。你只能这样做 当你想要哈希的字符串集通常具有有界大小时 只有10-1000。对于任意大的字符串词汇,你不能这样做。 由于你不能这样做,你实际上必须比较字符串以确定相等。

密码哈希具有很强的识别能力但不是设计的 要快。什么通常非常快,并具有良好的歧视 功能是CRC功能,大多数语言都很容易找到库 快速计算这些(使用字节表查找技术)。 我们使用CRC-32并且它对此非常有效(在2 ^ 32中基本上有1次机会发生哈希冲突,当字符串 不匹配)。您可以使用CRC-64,但具有额外的辨别能力 它提供的不会真正添加任何真正的功能。

答案 7 :(得分:0)

任何合适的哈希函数都可以很好地处理冲突。基本上,如果哈希导致存在多个答案的命中,那么该存储桶中存在潜在解决方案的链接列表,并且必然会在找到正确答案(如果存在)时减慢速度。

但是不要编写自己的哈希函数,使用那里的哈希函数。

哦,生成哈希应该只进行一次,我想。因为您有一个正在跟踪的事物的查找表,并且只需在添加新的“有趣”事物进行扫描时就必须更改哈希表。

答案 8 :(得分:0)

编辑:比我自己更好的评论(以及之前的评论),充其量也是多余的。

答案 9 :(得分:0)

我将Trie结构的上述建议作为本案例的最佳方法。计算上相当于一个完美的哈希,但在概念上更漂亮。这假设你的符号长度有限。

答案 10 :(得分:0)

FWIW,在我上一个高数据量项目中,我们发现使用一些经过严格调整的C代码对数据进行过滤,聚合和预分类是关键。我们所有的feed都进入了这个预处理器,在将大量数据传递给基于Java的系统进行处理之前,它负责简单的数据清理。基本上,预处理器就是您所要求的:确定感兴趣的记录,验证它们是否完整以及删除重复和清空。在高峰时段,预处理器可以消除我们每小时获得的8M左右记录中的高达20%(可能不是我想象你从股票市场提供的量)。我们原来的Java版本幸运地得到了一半(但它至少是“优雅的”!)

答案 11 :(得分:0)

它的价值。我解决了CMS(纽约证券交易所)和CQS(纳斯达克)符号系统特有的问题。符号根最多为6个字符,并且为大写。我的要求如下:

  • 数据将以未知符号到达
  • 收到数据后,计算用于比较的哈希值
  • 计算一次值,将值存储在地图中以供将来比较
  • 价值比较将是平等
  • 价值比较将与范围相对应。

例如如果GOOG的数据到达,则需要处理它并将其分发到符号范围[F-HAA]中的进程。 (F&lt; = GOOG&lt; = HAA)。我使用了一个具有低值(F)和高值(HAA)的范围类。我的Hash函数概念类似于将字符打包成字节,但是对于日志记录,网络和字节序目的,我选择unsigned long long作为我的存储类型。在调用此函数之前,符号用&#39; @&#39;填充。字符。 (IBM @@@)

unsigned long long SymbolToVal(std::string& str)
{
 size_t maxlen = 6; // Symbology constraint
 if (str.length() != maxlen) return 0;
 unsigned long long val;
 unsigned long long retval=0;
 int expon = maxlen*2; // ASCII val range (65-90)
 double factor = std::pow(10.0,expon);
 expon-=2;
 for (size_t i = 0; i < maxlen; i++)
 {
    val = (unsigned long long)factor * str[i];
    retval += val;
    factor = (unsigned long long) std::pow(10.0,expon);
    expon-=2;
  }
  return retval;
 }

强力方法是计算所有可能的符号对它们进行正确排序,并为它们分配一个整数,然后将它们存储在地图中。如果传入的数据仅包含整个域的一小部分(这是正常情况),则可能过度杀伤。