C# 使用Levenshtein距离检测字符串相似性(速度慢)
查询返回2100万条记录 我在这张桌子上绕来绕去的路要花很长时间。还有什么其他的解决方案C# 使用Levenshtein距离检测字符串相似性(速度慢),c#,.net,sql,string-comparison,C#,.net,Sql,String Comparison,查询返回2100万条记录 我在这张桌子上绕来绕去的路要花很长时间。还有什么其他的解决方案 SqlDataReader rd = DbInfo.DataRdr(Conn, "SELECT a.NAME AS ANAME, b.NAME AS BNAME, a.ID as AID, b.ID AS BUD " + "FROM myTable a JOIN myTable b ON a.NUM = b.NUM AND a.ID <> b.ID"); while (rd.Read())
SqlDataReader rd = DbInfo.DataRdr(Conn,
"SELECT a.NAME AS ANAME, b.NAME AS BNAME, a.ID as AID, b.ID AS BUD " +
"FROM myTable a JOIN myTable b ON a.NUM = b.NUM AND a.ID <> b.ID");
while (rd.Read())
{
if (rd["ANAME"].ToString().LevenshteinDistance(rd["BNAME"].ToString()) <= 10)
{
Logging.Write(...);
}
}
public static int LevenshteinDistance(this string s, string t)
{
if (s == null)
throw new ArgumentNullException("s");
if (t == null)
throw new ArgumentNullException("t");
int n = s.Length;
int m = t.Length;
int[,] d = new int[n+1,m+1];
if (n == 0 || m == 0)
return Math.Max(m, n);
for (int i = 0; i <= n; i++)
{
d[i, 0] = i;
}
for (int i = 0; i < m; i++)
{
d[0, i] = i;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
int cost = (t[j] == s[i]) ? 0 : 1;
d[i + 1, j + 1] = Math.Min(Math.Min(d[i, j + 1] + 1, d[i + 1, j] + 1), d[i, j] + cost);
}
}
return d[n, m];
}
SqlDataReader-rd=DbInfo.DataRdr(Conn,
选择a.NAME作为名称,b.NAME作为BNAME,a.ID作为辅助,b.ID作为BUD+
“从myTable a在a.NUM=b.NUM和a.ID上连接myTable b”);
while(rd.Read())
{
如果(rd[“ANAME”].ToString().levenshteInstance(rd[“BNAME”].ToString())您可以开始使用以下作为查询,具体取决于NUM
列实际相等的频率:
SELECT a.NAME AS ANAME, b.NAME AS BNAME, other things
FROM myTable a
JOIN myTable b
ON a.NUM = b.NUM
AND a.id < b.id
您正在将dt1.Rows[i][“Name”].ToString()
与dt1.Rows[j][“Name”].ToString()
进行比较,即使在i==j
时也是如此
尝试循环0优化:
1) 查看您的数据。也许您可以进行一些检查以更快地对无效对进行排序。如果Name
的长度变化超过10,您可以检查s.lenght
和t.Length
之间的差异是否大于10,并立即返回一个高距离(可能是int.MaxValue或仅100).无论如何,如果距离明显超出范围,那么计算距离就没有意义了
2) 寻找小的优化。在150k行上循环两次意味着225亿次迭代。小的更改可能会产生很大的影响。您可以尝试缓存到行对象,并通过使用Equals()删除ToString()
方法。我认为这比访问数据表的第I个元素150000次要快
for (int i = 0; i < dt1.Rows.Count; i++)
{
var outerRow = dt1.Rows[i];
for (int j = 0; i + 1 < dt1.Rows.Count; j++)
{
var innerRow = dt1.Rows[j];
if (Equals(outerRow["NUM"] == innerRow["NUM"]))
{
if (outerRow["Name"].ToString().LevenshteinDistance(innerRow.ToString()) <= 10)
{
Logging.Write(...);
}
}
}
for(int i=0;i 如果(outError[“Name”].ToString().LevenshTeInstance(innerRow.ToString())您可以做的最大改进是减少所考虑的解决方案空间。由于您希望最大距离为10,任何长度相差超过10的字符串都不可能符合以下条件:
SELECT a.NAME AS ANAME, b.NAME AS BNAME, a.ID as AID, b.ID AS BUD
FROM myTable a JOIN myTable b ON a.NUM = b.NUM AND a.ID < b.ID
WHERE length(a.NAME) - length(b.NAME) BETWEEN -10 AND 10;
选择a.NAME作为ANAME,b.NAME作为BNAME,a.ID作为AID,b.ID作为BUD
从myTable a在a.NUM=b.NUM和a.ID
接下来,分析您的代码,看看热点在哪里。一篇好的入门文章:
看一看
编辑
Chris还注意到,由于levenshtein(a,b)==levenshtein(b,a)
由于矩阵是对称的,您只需在join a.ID在完成本线程中提到的其他优化后,您可以将Levenshtein计算移动到服务器,并且只选择与编辑距离标准匹配的行。我需要这个函数在一个项目中,我创建了一个库。库中使用的编辑距离方法只需要n*2内存,而不是n*m内存
例如,即使在服务器上,您也只希望在字符串长度差<10时执行EditDistance计算,因此请首先检查该值。
差不多
SELECT a.NAME as NameA, b.NAME as NameB
FROM table a
JOIN table b ON a.NUM = b.NUM
WHERE a.Id < b.Id
AND length(a.NAME) - length(b.NAME) BETWEEN -10 AND 10 OR
EditDistance(a.Name, b.Name) < 10
选择a.NAME作为名称a,b.NAME作为名称b
从表a
在a.NUM=b.NUM上联接表b
其中a.Id
SELECT*一开始就不好。你能改进一下查询吗?我们能看看levenshteInstance函数的代码吗?@John haha……他大概是说其中一个应该是“j”.which i?:P好的,我修复了这个问题,不会更快,但值得注意的是,这在纯SQL中是可能的:您的查询返回700多万条记录,我的表有130000条记录。原始代码执行时间较长的原因是您执行的查询返回130000条记录,然后执行Levenshtein函数130000*130000=169亿次。700万是显著的改进(我的查询返回所有要比较的记录对)!正如另一个答案所说,请更好地编写LevenshteInstance,这样就不会有太多麻烦。如果您只关心距离是否大于10,则可以在大量情况下尽早停止距离计算。count返回2100万行显示我可以优化Lev方法吗我当前的问题是系统。OutOfMemory当我运行Chris Cunningham queryI对代码做了一些更改时抛出的异常错误,请参阅上面的问题代码。这些更改解决了内存异常错误,但又很慢。看起来这是它能得到的最快的速度,我删除了LevenShtein方法,它对速度影响不大。
SELECT a.NAME as NameA, b.NAME as NameB
FROM table a
JOIN table b ON a.NUM = b.NUM
WHERE a.Id < b.Id
AND length(a.NAME) - length(b.NAME) BETWEEN -10 AND 10 OR
EditDistance(a.Name, b.Name) < 10