所以我有一个递归代码,可以为2条DNA链提供最佳对齐,但问题是它的执行速度非常慢(我需要它递归)。然后我在麻省理工学院网站上看到结果是附加的,这对我很好,但后来我想了一下,我发现有一个问题:
麻省理工学院网站说,对于给定的溢出(i,j):
first_strand(0,i)和second_strand(0,j)对齐
+
first_strand(i,len)和second_strand(j,len)alignment
等于
first_strand和second strand alignment
但:
GTC GTAA
与GTA对齐的G是G--和GTA 具有A对齐的TC是TC和A- 结果= G - TC和GTAA -真正的最佳结果= GTC-GTAA
任何人都可以解释他们在麻省理工学院网站上的含义吗?我可能错了!
答案 0 :(得分:5)
我假设你在谈论this link。
如果是这样的话,请仔细阅读数百次;-)这是“添加” 你只考虑分割修正的路线特定(i, j)
对。
在您假设的反例中,您首先打破G
的初始GTC
和GTA
的初始GTAA
。然后,G--
是将GTC
更改为G
的最短途径。精细。继续使用相同的拆分,您仍需要将剩余的右侧部分对齐:TC
和A
。也没关系。
这是 no 声称这是最佳分割。只有声称它是最好的对齐,因为你只考虑特定的分割。
这是动态编程方法中的一小步,这是你缺少的部分。仍需要计算所有可能的拆分的最佳对齐方式。
动态编程起初很棘手。你不应该期望从盯着电报幻灯片中学到它。阅读一本真正的教科书,或在网上搜索教程。
注释表明这个“必须”的代码是递归的。哦,好吧; - )
警告:我只是将它们放在一起来说明加速合适的递归函数的一般过程。它几乎没有经过测试。
首先是一个完全天真的递归版本:
def lev(a, b):
if not a:
return len(b)
if not b:
return len(a)
return min(lev(a[:-1], b[:-1]) + (a[-1] != b[-1]),
lev(a[:-1], b) + 1,
lev(a, b[:-1]) + 1)
我将在此处讨论的所有游戏中使用"absd31-km"
和"ldk3-1fjm"
作为参数。
在我的盒子上,使用Python 3,这个简单的函数在大约1.6秒后返回7。它可怕慢。
最明显的问题是无休止的重复字符串切片。索引中的每个:
都需要与被切片的字符串的当前长度成比例的时间。所以第一个改进是传递字符串索引。由于代码总是切掉字符串的前缀,我们只需要传递“字符串结尾”索引:
def lev2(a, b):
def inner(j1, j2):
if j1 < 0:
return j2 + 1
if j2 < 0:
return j1 + 1
return min(inner(j1-1, j2-1) + (a[j1] != b[j2]),
inner(j1-1, j2) + 1,
inner(j1, j2-1) + 1)
return inner(len(a)-1, len(b)-1)
好多了!此版本在“仅”中返回7,大约1.44秒。仍然非常缓慢,但比原来更好。它的优点是会在较长的琴弦上增加,但是谁在乎; - )
我们差不多完成了!现在要注意的重要一点是,该函数在运行过程中多次传递相同的参数。我们在“备忘录”中捕获那些以避免所有冗余计算:
def lev3(a, b):
memo = {}
def inner(j1, j2):
if j1 < 0:
return j2 + 1
if j2 < 0:
return j1 + 1
args = j1, j2
if args in memo:
return memo[args]
result = min(inner(j1-1, j2-1) + (a[j1] != b[j2]),
inner(j1-1, j2) + 1,
inner(j1, j2-1) + 1)
memo[args] = result
return result
return inner(len(a)-1, len(b)-1)
该版本在大约0.00026秒内返回7,比lev2
快5000倍。
现在,如果您研究了基于矩阵的算法,并稍微眯一眼,您会看到lev3()
有效构建一个二维矩阵映射索引对以导致它的memo
字典。除了递归版本以更复杂的方式构建矩阵之外,它们实际上是相同的。另一方面,递归版本可能更容易理解和推理。请注意,您发现的幻灯片称为memoization aporoach“自上而下”,嵌套循环矩阵称为“自下而上”。这些都很好描述。
你还没有说过你的递归函数是如何工作的,但如果它遭受任何类似的递归过量,你应该能够使用类似的技术获得类似的加速: - )