Python 使用动态规划和二维数组编辑距离算法 - 输出不是最优的
Python Edit distance algorithm with dynamic programming and 2D Array - Output is not optimal
我遇到了编辑距离(Levenshtein distance)的问题。我查看了其他类似的 Whosebug 问题,并确定我的问题与它们不同——无论是使用的语言还是方法。
我使用了一个 2D 数组 来比较两个字符串,并使用 动态编程 来存储以前的值。如果字符串索引中的 i 和 j 匹配,它将输出 0,因为我们不需要做任何事情;否则,输出为1。如下图,橙色箭头表示匹配。
(根据答案的建议编辑以下代码)
def edit_distance(source, target):
n = len(target)+1
m = len(source)+1
# let D denote the 2-dimensional array, m is the column, n is row
D = [ [0]*m for _ in range(n)]
# the loop inside is the target string, we operate this
# while the loop outside is the source string
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
# match, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
# mismatch, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
return D[n-1][m-1]
print(edit_distance("distance", "editing"))
然而,我的代码最终输出的是8,而字符串“editing”和“distance”之间的最佳编辑距离应该是5,我很困惑。能不能从动态规划的角度帮忙解决一下?
你有 2 个错误。
首先是初始化。你用 0 填充所有内容,但是当你想填充 D[1][m] 时,你查看上面的单元格(它应该是 m)并找到一个 0。确保正确填充了边框。
其次,您的迭代已关闭。 range(1, n) over 'editing' 会给你 'diting'。要将 N 和 M 固定为 1 (n=len(target) + 1
) 并在比较中使用 target[i-1] == source[j-1]
.
啊,看来我找到了解决办法,我现在必须回答我自己的问题了。 (有些地方我还是很困惑,回答这个问题只是为了简单介绍一下新的实现方式,以节省其他好心人的时间)
所以首先,我在原代码中遗漏了一个条件,即如果两个字符串输入之一为空怎么办?然后我们必须插入其他字符串中的所有内容。从此以后,最佳编辑距离就是另一个字符串的长度。
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
另外,关于代码的原始for循环,我从GeeksforGeeks中吸取了错误。如果我的理解是正确的,他们是说如果两个指标(i和j)一致,我们需要做的就是在图(i-1和j)上沿对角线向上移动 -1) 不添加任何计数。
否则,如果索引不匹配,我们将移动到 i-1、j-1 的方向或依赖地对角线向上。我在这一点上是对的,除了计数是在移动之后添加的,而我是在移动过程中添加的。
我仍然有点不确定它是如何工作的,但是我将比较下面的两种算法,如果有人可以在评论中进一步解释它,我将不胜感激。
我原来的 for 循环(出现在问题中)
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
下面是新的for循环,经测试输出正确:
if target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
如果有人能进一步解释这是如何工作的,我将不胜感激,因为我对新代码的了解还很肤浅
最终代码:
def edit_distance(target, source):
m = len(target)+1
n = len(source)+1
D = [[0 for x in range(n)] for x in range(m)]
for i in range(m):
for j in range(n):
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
elif target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
return D[m-1][n-1]
print(edit_distance("distance", "editing"))
# output = 5, which is correct
我遇到了编辑距离(Levenshtein distance)的问题。我查看了其他类似的 Whosebug 问题,并确定我的问题与它们不同——无论是使用的语言还是方法。
我使用了一个 2D 数组 来比较两个字符串,并使用 动态编程 来存储以前的值。如果字符串索引中的 i 和 j 匹配,它将输出 0,因为我们不需要做任何事情;否则,输出为1。如下图,橙色箭头表示匹配。
(根据答案的建议编辑以下代码)
def edit_distance(source, target):
n = len(target)+1
m = len(source)+1
# let D denote the 2-dimensional array, m is the column, n is row
D = [ [0]*m for _ in range(n)]
# the loop inside is the target string, we operate this
# while the loop outside is the source string
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
# match, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
# mismatch, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
return D[n-1][m-1]
print(edit_distance("distance", "editing"))
然而,我的代码最终输出的是8,而字符串“editing”和“distance”之间的最佳编辑距离应该是5,我很困惑。能不能从动态规划的角度帮忙解决一下?
你有 2 个错误。
首先是初始化。你用 0 填充所有内容,但是当你想填充 D[1][m] 时,你查看上面的单元格(它应该是 m)并找到一个 0。确保正确填充了边框。
其次,您的迭代已关闭。 range(1, n) over 'editing' 会给你 'diting'。要将 N 和 M 固定为 1 (n=len(target) + 1
) 并在比较中使用 target[i-1] == source[j-1]
.
啊,看来我找到了解决办法,我现在必须回答我自己的问题了。 (有些地方我还是很困惑,回答这个问题只是为了简单介绍一下新的实现方式,以节省其他好心人的时间)
所以首先,我在原代码中遗漏了一个条件,即如果两个字符串输入之一为空怎么办?然后我们必须插入其他字符串中的所有内容。从此以后,最佳编辑距离就是另一个字符串的长度。
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
另外,关于代码的原始for循环,我从GeeksforGeeks中吸取了错误。如果我的理解是正确的,他们是说如果两个指标(i和j)一致,我们需要做的就是在图(i-1和j)上沿对角线向上移动 -1) 不添加任何计数。
否则,如果索引不匹配,我们将移动到 i-1、j-1 的方向或依赖地对角线向上。我在这一点上是对的,除了计数是在移动之后添加的,而我是在移动过程中添加的。
我仍然有点不确定它是如何工作的,但是我将比较下面的两种算法,如果有人可以在评论中进一步解释它,我将不胜感激。
我原来的 for 循环(出现在问题中)
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
下面是新的for循环,经测试输出正确:
if target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
如果有人能进一步解释这是如何工作的,我将不胜感激,因为我对新代码的了解还很肤浅
最终代码:
def edit_distance(target, source):
m = len(target)+1
n = len(source)+1
D = [[0 for x in range(n)] for x in range(m)]
for i in range(m):
for j in range(n):
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
elif target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
return D[m-1][n-1]
print(edit_distance("distance", "editing"))
# output = 5, which is correct