将算法的Big O符号从O(n ^ 2)改进到更好的

时间:2018-06-28 13:28:22

标签: string algorithm time-complexity big-o

我正在寻找一种改进我目前拥有的算法的方法,尽管它可以工作,但目前具有O(n ^ 2)的复杂度。我希望尽可能降低这种复杂性,或者改善/更改算法本身以提高运行时间。

我有一个字符串列表,每个字符串包含多个单词,最终目标是找到这些字符串之间的“匹配项”,并根据百分比“相似度”进行排序。

假设我有以下字符串:

“世界尽头”
“旅程的开始”
“时间的尽头”
“今天我们离开这个世界的时间”

我的算法执行以下步骤:

  • 遍历每个字符串,将每个字符串分解为组成单词,然后按字母顺序对这些单词重新排序(在整个算法中,大小写不敏感)。 (即“世界尽头”变为“世界尽头”。“我们今天离开这个世界的时间”变为“今天我们离开这个世界的时间”等)
  • 出于商业原因,某些单词会从处理后的字符串中删除。通常是代词和其他类似的词-即a等,因此“世界末日”变为“世界末日”。
  • 我们现在有了一个字符串列表,这些字符串已从组成词中按字母顺序分解和重新组合,并删除了特定的非必需词。
  • 首先,我可以简单地查看列表中是否有完全相同的重复项。这是微不足道的,可让我确定那些有效匹配100%的字符串。
  • 但是,现在,该算法变得更困难,最慢。我必须遍历字符串列表,将每个字符串与列表中的每个其他字符串(即嵌套循环)进行比较,以确定每个字符串在被比较的两个单词中有多少个共同点。即,在比较“世界末日”和“世界末日”时,共有66.6%,因为两个字符串在三个词中有两个是相同的。将“世界尽头”与“今天我们今天离开这个世界”进行比较时,我们发现两个字符串之间只有一个共同的词(由于每个字符串中的词数不同,在这种情况下,实际百分比是根据两者之间的平均值-大约有22%的通用性。

最终,我剩下了成对的字符串(开始列表中所有字符串的所有可能配对)以及它们之间匹配的百分比值。然后,我可以丢弃所有低于某个阈值的那些匹配项,并且仅使用那些高于阈值的匹配项。该阈值是用户定义的,整个算法用作“过滤”非常大的一组数据的方法,从而使人眼只能处理最初看起来非常匹配的那些数据。

您可以从算法的嵌套循环(即O(n ^ 2))部分中想象到,这非常慢,并且随着输入大小的增加而变得相当慢。

是否有任何方法可以改进此算法的Big O或对算法产生相同的输出以提高运行时复杂性进行任何更改?

1 个答案:

答案 0 :(得分:0)

如果在所有计算中都随身携带字符串,则会带来额外的复杂性,这使得最后一个操作不是O(M ^ 2)而是O(M ^ 2 * sizeof(句子)* AvgLength(word))< / p>

让我们看看(概念代码)

std::vector<std::set<int>> sSets;
sentenceSets.reserve(sentences.size());

for(auto& sentence : sentences) { // O(m)
  std::vector<const char *> words = SplitWord(sentence); // O(n) needs to go through all letters.
  sSet.emplace_back();
  for(auto& word: words) {
    int wordNo = LookUp(word); // table of all words, with entries of 0 for unwanted words. O(log AllWords)
    if (wordNo)
      sSet.back().insert(wordNo); // also removes duplicates. O(Log(#diff words in sentence))
  }
} 

如果您认为所有可能单词的哈希表都能正常工作,则总计O(m Log(AllWords)avgWordLen)或O(m crashFactor avgWordLen)。

LookUp为以后的所有比较保存了因子O(字中的字母)。

for(const auto& theSet : sSet) { // O(sSet.size()
  for(const auto& cmpSet : sSet) { // O(sSet.size()
    std::vector<int> intersect;
    std::set_intersection(theSet.begin(), theSet.end(),
                          cmpSet.begin(), cmpSet.end(),
                          std::back_insert<std::vector<int>>(intersect)); // O(set.size())
    StoreRes(theSet, cmpSet, intersect);
  }
}

这里总共是O(sSet.size()^ 2 * O(set.size())。 可以优化为只运行O(sSet.size()* sSet.size()/ 2),因为表是对称的。

使用LookUp在这里节省了因子O(字长)。

为了更快地进行实际操作,可以将std :: set替换为flat_set。