【问题标题】:algorithm to identify string matches with hashing and no false positives用散列法识别字符串匹配且无误报的算法
【发布时间】:2015-03-16 09:16:55
【问题描述】:

我需要查找给定的字符串是否与列表中的字符串匹配,而列表中没有字符串;基本上我需要对字符串进行哈希处理并仅与哈希列表匹配。问题在于确保没有误报,因此只会找到完全匹配的字符,而不会找到任何其他字符集。使用实际的字符串列表当然很容易,即使是简单的二进制搜索也可以,但我想要一种在没有实际字符存在(即预先计算)的情况下工作的算法。布隆过滤器不能保证某些任意字符集可能不匹配。

更新:这类似于只存储密码哈希,然后对输入的密码进行哈希处理,然后将其与哈希列表进行比较以查看密码是否是其中之一(诚然,这不是通常使用的密码)。此要求的原因是不必发送实际文本,只需发送哈希值。

更新 2:如果没有完美的哈希函数,还有其他方法吗?我有数十万个条目,很难找到完美的哈希。也许类似于布隆过滤器但有更好的保证?

【问题讨论】:

  • 您可能正在寻找perfect hash function
  • 我确实读过这些,但我如何确定一个合理的哈希函数?
  • 我不确定您所说的“合理”是什么意思。这里的“完美”并不意味着“非常非常好”——它是一个艺术术语。在给定输入集的情况下,有一些算法可以构建完美的哈希函数 - 查找其中之一。
  • 是的,我知道这意味着什么,我有数十万个键,要为很多键找到完美的散列并不是一件容易的事。
  • @IgorTandetnik(和 ahwulf):完美的哈希值并不能避免误报。他们所做的只是保证您只需将提供的密钥与单个候选密钥进行比较。如果您想避免误报,您仍然需要测试的候选键。

标签: c++ string algorithm hash


【解决方案1】:

一个好的密码散列函数(有足够的比特)会使错误匹配的概率极小;足够小以至于暴力攻击基本上是不可能的。大多数安全系统都认为这样的机制就足够了。

如果您想要绝对保证不会出现误报,那么您实际上需要包含足够的数据来验证输入;不能比目标字符串短(但不必更大)。实际上,您需要加密目标字符串。由于加密密钥和加密字符串都是可见的,为了避免有人简单地解密加密字符串,您需要使用asymmetric cipher。这些计算成本很高,但这对您的环境来说可能不是问题。

【讨论】:

  • 在运行时性能不是问题。在构建时,我可以承担任何时间,只需要很少做。让我看看那个。
【解决方案2】:

任何完美的哈希都可以。跟进一个字符串比较以验证它不是误报。

【讨论】:

  • 完美哈希有误报吗?我可能误解了这个词。
  • 如何在“没有实际字符的情况下”比较字符串?
  • 我没有要比较的字符串,这就是整个问题。我只能拥有哈希(或其他)。
  • 你可以为 N 个字符串创建一个完美的散列。如果您查找的单词不是这 N 个字符串的成员,则可能会发生冲突。
【解决方案3】:

这是一个“几乎完美”的哈希算法:

MPQ Hash

在星际争霸存档文件中使用。该算法非常高效,并且碰撞可能性非常低(平均约为 1:18889465931478580854784)。

这是该算法的工作原理。

1.计算三个哈希(偏移哈希和两个校验哈希)并将它们存储在变量中。

2.移动到偏移哈希的入口

3.该条目是否未使用?如果是这样,停止搜索并返回“找不到文件”。

4.这两个校验哈希是否与我们要查找的文件的校验哈希匹配?如果是,则停止搜索并返回当前条目。

5.移动到列表中的下一个条目,如果我们在最后一个条目上,则绕到开头。

6.我们刚刚移动到的条目是否与偏移哈希相同(我们是否查看了整个哈希表?)?如果是这样,停止搜索并返回“找不到文件”。

7.返回步骤 3。

这里是散列和散列表函数:

unsigned long HashString(char *lpszFileName, unsigned long dwHashType)
{
    //lpszFileName is the string to be hashed.
    //dwHashType will change the hash value according to hash mode.
    //You can see how it's used in the beginning of GetHashTablePos().
    unsigned char *key = (unsigned char *)lpszFileName;
    unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;
    int ch;

    while(*key != 0)
    {
        ch = toupper(*key++);   //Convert every character to upper case.
        //dwHashType will change the hash value in different hashing modes.
        //(Whether to calculate the position or to verify.)
        seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
        seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
    }
    return seed1;
}

int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize)
{
    const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
    //nHash controls where the hash value of the string should be stored in the hash table.
    //nHashA and nHashB are used for verifying the match
    int nHash = HashString(lpszString, HASH_OFFSET), 
        nHashA = HashString(lpszString, HASH_A), 
        nHashB = HashString(lpszString, HASH_B), 
        nHashStart = nHash % nTableSize, nHashPos = nHashStart;

    while (lpTable[nHashPos].bExists)
    {
        if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB)
            return nHashPos;   //If found, return the entry index
        else
            nHashPos = (nHashPos + 1) % nTableSize;   //Not found, move to next position
         
        if (nHashPos == nHashStart)
            break;  //Reach the beginning of table, stop searching.
    }

    return -1; //Error value
}

【讨论】:

    【解决方案4】:

    您可以考虑组合一个bloom filter。这基本上是一组哈希算法和哈希表,当它们作为一个组时,可以给出非常高的正确匹配概率。它不是 100%,但您可以随心所欲地接近它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 2015-05-02
      • 2020-10-01
      • 1970-01-01
      • 2013-05-29
      相关资源
      最近更新 更多