Levenshtein 两个文件的距离花费了太多时间

Levenshtein Distance on two files taking too much time

本文关键字:距离 时间 太多 文件 两个 Levenshtein      更新时间:2023-10-16

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

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

将文件转换为字符串所花费的时间几乎是 95 倍,这使这成为我项目中的瓶颈

任何帮助将不胜感激 我对C,C++和Python很满意。如果您能指出我的任何来源,我将不胜感激

以下是我用于计算列文施泰因距离的函数的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;
}

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

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

我用"Lorem ipsum sum总和"生成器创建了两个文件,每个文件有1000个单词。每个文件的文件大小为 ~6kB。

结果在眨眼间就出来了。

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

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

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

然后我们打印列文森距离的结果。

请参阅下面的代码:

#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;
}

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

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

输出:

1