【问题标题】:C++ strange results - brute force is quicker than Rabin-Karp...?C++ 奇怪的结果 - 蛮力比 Rabin-Karp 更快......?
【发布时间】:2020-03-26 07:52:55
【问题描述】:

目前正在为一个 uni 模块开发一个字符串搜索程序,并且我已经成功地实现了这些算法,至少到了他们能够始终如一地找到字符串的程度。我实现了 Boyer Moore 和 Rabin Karp。当我的一个同学遇到这个确切的问题时,我也投入了蛮力,并意识到我遇到了同样的问题 - 蛮力比单词列表上的 Rabin-Karp 更快。

Rabin-Karp 似乎花费了最多时间来执行滚动哈希,起初我很好奇我是否只是有很多碰撞,但我设法用一个巨大的素数将碰撞减少到 3。由于质数的大小,我假设这增加了一点时间,但滚动散列导致问题似乎很明显。

这是滚动哈希部分:

//hashes don't match, rehash using rolling hash to move on to next string section
  if (counter < (stringLength - patternLength)) { 

            stringHash = (MAXCHAR *(stringHash - stringFile[counter] * hash) + stringFile[counter + patternLength]) % prime;


            if (stringHash < 0) {

                stringHash += prime;    //when hash value is negative, make it positive
            }

        }

        if (!found) {

            counter++; 
        }

我想尝试搜索一个巨大的文本文件,所以我使用了 Rockyou 词汇表,Boyer Moore 对此非常满意,而 Rabin-Karp 只用了不到一秒钟。蛮力花费的时间不到 Rabin-Karp 的一半,尽管这对我来说没有意义?

我是否误解了应该如何应用这些算法,或者我正在使用的滚动哈希过程是否存在问题?

【问题讨论】:

  • 顺序表示法省略了常数。如果常数对于 O(1) 来说足够高,那么在 n 变得足够大之前,具有低常数的 O(n) 会更慢。确保您的实现是正确的,并且您正在使用足够大的数据集进行测试。

标签: algorithm brute-force string-search rabin-karp


【解决方案1】:

蛮力字符串搜索是 Rabin-Karp 的特例,具有恒定哈希函数(因此每个滚动哈希都匹配)。

两种算法的最坏情况复杂度相同,“平均情况”的大多数定义的平均情况复杂度也是如此。

在这些情况下,由于计算和检查良好哈希的开销,Rabin-Karp 将花费更长的时间。

与 Rabin-Karp 相比,蛮力的问题在于现实生活中有时会发生不良情况。例如,如果您正在搜索路径名,那么您的模式可能具有与文件中许多或大部分路径名和部分路径名相同的长前缀,这将使蛮力花费很长时间时间。

有了 Rabin-Karp,现实生活中不太可能发生不良情况。它们实际上只发生在“对抗性”条件下,在这种情况下,文件和模式是有目的地构建的,需要很长时间,并且对您使用的散列函数有特定的了解。

即便如此... Rabin-Karp 并不是一个很好的单模式搜索算法。当您同时搜索多个字符串并且可以在潜在匹配的字典中查找滚动哈希时,它会变得更加有用。

【讨论】:

  • 嗨,马特,感谢您的回答。这让我去尝试使用不同的数据集来使蛮力表现不佳(我使用了大约 100 万个以 'ja' 结尾的'a' 字符,并且模式搜索是'aaaaaaaaaja',是的,Rabin-Karp 有点比蛮力更好,就像你用公共前缀说的那样。Boyer Moore 在这个数据集上最差——我认为这种类型的搜索是 Boyer Moore 最坏的情况是正确的吗?
  • Boyer-Moore 唯一真正糟糕的情况是该模式出现多次。你的情况还不错,但它破坏了优化,所以它不能跳过。
猜你喜欢
  • 2021-03-02
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多