【问题标题】:Levenshtein Distance on two files taking too much timeLevenshtein 两个文件的距离太长
【发布时间】:2020-06-29 02:34:29
【问题描述】:

我是编程新手,我正在构建一个文件相似度查找器,它可以找出两个文件的相似程度。 到目前为止,我将文件存储为两个字符串,然后使用 levenshtein distance 来找出文件的相似程度。

问题是,没有levenshtein距离的执行时间是206ms,这是由于文件到字符串的转换。 当我使用 levenshtein 距离时,执行时间高达 19504 毫秒

将文件转换为字符串所需的时间将近 95 倍,这使我的项目成为瓶颈

任何帮助将不胜感激 我熟悉 C、C++ 和 Python。如果您能指出任何来源,我将不胜感激

这是我用于计算 Levenshtein 距离的函数的 C++ 代码:

//LEVENSHTEIN
int levenshtein(std::string a, std::string b){
  int len_a = a.length();
  int len_b = b.length();
  int d[len_a + 1][len_b+1];

  for(int i = 0; i < len_a + 1; i++)
    d[i][0] = i;

  for(int j = 0; j < len_b + 1; j++)
    d[0][j] = j;

  for(int i = 1; i < len_a + 1; i++){
    for(int j = 1; j < len_b + 1; j++){
      if(a[i - 1] == b[j - 1]){
        d[i][j] = d[i - 1][j - 1];
      }
      else{
        d[i][j] = 1 + min(min(d[i][j-1],d[i-1][j]),d[i-1][j-1]);
      }
    }
  }

  int answer = d[len_a][len_b];

  return answer;
}

我只需要比较两个文件,而不是更多。我在 levenshtein 中读到了 trie 的用法,但这对于将多个字符串与源进行比较很有用。除此之外,我运气不佳

【问题讨论】:

  • 你为什么用 python 标记这个?
  • 我也接受参考 python 的答案。我愿意用两种语言制作程序
  • 你很接受你。但是,我们不会用任何特定语言为您编写整段代码。如果您的问题中没有 python,请删除标签。 (c 也是一种不同的语言,也请删除该标签)。
  • 这使问题偏离主题,因为您正在寻求外部资源的建议。
  • 您使用的算法不是最有效的(只需要两行,而不是完整的矩阵)。但在任何情况下,Levenshtein 距离的计算速度都不能比O(n^2) 快,所以不要指望有任何显着的改进。有一些方法可以在大致线性的时间内计算 an approximate value - 看看这是否足够好。

标签: python c++ c optimization levenshtein-distance


【解决方案1】:

我将向您展示一个 C++ 解决方案。使用的语言是 C++17。编译器是 MS Visual Studio Community 2019。在发布模式下编译并开启所有优化。

我创建了两个文件,每个文件都有 1000 个单词,每个文件都有一个“Lorem ipsum sum”生成器。每个文件的文件大小约为 6kB。

结果一眨眼就出来了。

我正在使用稍微修改过的 levensthein 函数,并且还使用了更易读的变量名。我不使用 VLA(可变长度数组),因为这在 C++ 中无效。我改用std::vector,它具有更出色的功能。

在main中,我们可以看到驱动代码。首先,我们打开 2 个输入文件,并检查它们是否可以打开。如果没有,我们会显示错误消息并退出程序。

然后我们使用std::string范围构造函数和std::istreambuf_iterator将这2个文本文件读入2个字符串。我不知道有什么更简单的方法可以将完整的文本文件读入std::string

然后我们打印 Levensthein 距离的结果。

请看下面的代码:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iterator>

// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
    // First get the string lengths
    const size_t lengthString1{ string1.size() };
    const size_t lengthString2{ string2.size() };

    // If one of the string length is 0, then return the length of the other
    // This results in 0, if both lengths are 0
    if (lengthString1 == 0) return lengthString2;
    if (lengthString2 == 0) return lengthString1;

    // Initialize substitition cost vector
    std::vector<size_t> substitutionCost(lengthString2 + 1);
    std::iota(substitutionCost.begin(), substitutionCost.end(), 0);

    // Calculate substitution cost
    for (size_t indexString1{}; indexString1 < lengthString1; ++indexString1) {
        substitutionCost[0] = indexString1 + 1;
        size_t corner{ indexString1 };

        for (size_t indexString2{}; indexString2 < lengthString2; ++indexString2) {
            size_t upper{ substitutionCost[indexString2 + 1] };
            if (string1[indexString1] == string2[indexString2]) {
                substitutionCost[indexString2 + 1] = corner;
            }
            else {
                const size_t temp = std::min(upper, corner);
                substitutionCost[indexString2 + 1] = std::min(substitutionCost[indexString2], temp) + 1;
            }
            corner = upper;
        }
    }
    return substitutionCost[lengthString2];
}

// Put in your filenames here
const std::string fileName1{ "text1.txt" };
const std::string fileName2{ "text2.txt" };

int main() {

    // Open first file and check, if it could be opened
    if (std::ifstream file1Stream{ fileName1 }; file1Stream) {

        // Open second file and check, if it could be opened
        if (std::ifstream file2Stream{ fileName2 }; file2Stream) {

            // Both files are open now, read them into strings
            std::string stringFile1(std::istreambuf_iterator<char>(file1Stream), {});
            std::string stringFile2(std::istreambuf_iterator<char>(file2Stream), {});

            // Show Levenstehin distance on screen
            std::cout << "Levensthein distance is: " << levensthein(stringFile1, stringFile2) << '\n';
        }
        else {
            std::cerr << "\n*** Error. Could not open input file '" << fileName2 << "'\n";
        }
    }
    else {
        std::cerr << "\n*** Error. Could not open input file '" << fileName1 << "'\n";
    }
    return 0;
}

【讨论】:

    【解决方案2】:

    有一个名为nltk 的包。看看吧。

    from nltk import distance
    print(distance.edit_distance('aa', 'ab'))
    

    输出:

    1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-20
      • 1970-01-01
      • 2016-03-28
      • 2014-04-14
      • 2014-08-04
      • 2015-01-28
      • 1970-01-01
      相关资源
      最近更新 更多