只需对您的代码进行最少的更改:
def lcs_recursive(seq1, seq2, idx1 = 0, idx2 = 0, lcs = ''):
if idx1 == len(seq1) or idx2 == len(seq2):
return lcs
if seq1[idx1] == seq2[idx2]:
return lcs_recursive(seq1, seq2, idx1 + 1, idx2 + 1, lcs + seq1[idx1])
else:
option1 = lcs_recursive(seq1, seq2, idx1 + 1, idx2, lcs)
option2 = lcs_recursive(seq1, seq2, idx1, idx2 + 1, lcs)
return max(option1, option2, key=len)
seq1 = 'serendipitous'
seq2 = 'precipitation'
print(lcs_recursive(seq1, seq2)) # reipito
由于您可能多次使用相同的索引对调用 lcs_recursive,因此如果使用 Python >= 3.9 则 functools.cache 或如果使用 Python >= 3.2 则 functools.lru_cache 可能会提高性能。
例如,如果seq1 = 'ab' 和seq2 = 'bd',lcs_recursive('ab', 'bd') 最终将调用lcs_recursive('ab', 'bd', 2, 1) 两次(您可以通过在函数中执行print(idx1, idx2) 的第一件事来看到这一点)。使用下面的@lru_cache 优化,在第一次调用(idx1=2, idx2=1)(即lcs_recursive('ab', 'bd', 2, 1))时,计算并存储与(idx1=2, idx2=1) 对应的结果(在缓存中,您可以将其视为字典)。在随后调用同一索引对 ((idx1=2, idx2=1)) 时,会查找存储的结果,因此不必重新计算。一般来说,lru_cache 会根据输入来记忆结果。这就是为什么在下面的代码中,函数helper 只需要idx1 和idx2 - 以使lru_cache 更容易正常工作。
from functools import lru_cache
def lcs_recursive(seq1, seq2):
@lru_cache
def helper(idx1 = 0, idx2 = 0):
if idx1 == len(seq1) or idx2 == len(seq2):
return ''
if seq1[idx1] == seq2[idx2]:
return seq1[idx1] + helper(idx1 + 1, idx2 + 1)
option1 = helper(idx1 + 1, idx2)
option2 = helper(idx1, idx2 + 1)
return max(option1, option2, key=len)
return helper()
seq1 = 'serendipitous'
seq2 = 'precipitation'
print(lcs_recursive(seq1, seq2)) # reipito
作为比较,seq1 = 'serendipitous' 和 seq2 = 'precipitation' lcs_recursive_uncached(seq1, seq2) 进行 1119564 次递归调用,而 lcs_recursive_cached(seq1, seq2) 仅进行 184 次递归调用!:
number_of_calls_uncached = 0
def lcs_recursive_uncached(seq1, seq2, idx1 = 0, idx2 = 0, lcs = ''):
global number_of_calls_uncached
number_of_calls_uncached += 1
if idx1 == len(seq1) or idx2 == len(seq2):
return lcs
if seq1[idx1] == seq2[idx2]:
return lcs_recursive_uncached(seq1, seq2, idx1 + 1, idx2 + 1, lcs + seq1[idx1])
else:
option1 = lcs_recursive_uncached(seq1, seq2, idx1 + 1, idx2, lcs)
option2 = lcs_recursive_uncached(seq1, seq2, idx1, idx2 + 1, lcs)
return max(option1, option2, key=len)
seq1 = 'serendipitous'
seq2 = 'precipitation'
print(number_of_calls_uncached) # 0
print(lcs_recursive_uncached(seq1, seq2)) # reipito
print(number_of_calls_uncached) # 1119564
from functools import lru_cache
number_of_calls_cached = 0
def lcs_recursive_cached(seq1, seq2):
@lru_cache
def helper(idx1 = 0, idx2 = 0):
global number_of_calls_cached
number_of_calls_cached += 1
if idx1 == len(seq1) or idx2 == len(seq2):
return ''
if seq1[idx1] == seq2[idx2]:
return seq1[idx1] + helper(idx1 + 1, idx2 + 1)
option1 = helper(idx1 + 1, idx2)
option2 = helper(idx1, idx2 + 1)
return max(option1, option2, key=len)
return helper()
seq1 = 'serendipitous'
seq2 = 'precipitation'
print(number_of_calls_cached) # 0
print(lcs_recursive_cached(seq1, seq2)) # reipito
print(number_of_calls_cached) # 184