如果你的 RNA 不是很长(一千个碱基可能还可以;几十万个肯定不行),你可以使用简单的 O(n^3) 算法。O(n^3) 意味着执行时间在最坏的情况下与碱基数的立方成正比。作者提到嵌套循环在很大程度上暗示了这种简单但相当缓慢的方法。
def find_loops(rna, min_pairs=3, min_loop=3):
n = len(rna)
result = []
for loop_start in xrange(min_pairs, n - min_pairs - min_loop + 1):
for loop_end in xrange(loop_start + min_loop, n - min_pairs):
if (loop_end - loop_start < min_loop + 2 or
not base_pair(rna[loop_start], rna[loop_end - 1])):
max_pairs = min(loop_start, n - loop_end)
for k in xrange(max_pairs):
if not base_pair(rna[loop_start - k - 1], rna[loop_end + k]):
break
else:
k = max_pairs
if k >= min_pairs:
result.append((loop_start - k, k, loop_end - loop_start))
return result
def base_pair(x, y):
return (x == 'A' and y == 'U' or
x == 'C' and y == 'G' or
x == 'G' and y == 'C' or
x == 'U' and y == 'A')
这会遍历所有可能的 RNA 环的开始和结束,然后在两个方向上从潜在环的末端走开,只要碱基仍然配对。当它到达一对不匹配的碱基时,它会停下来检查它是否至少有最少的碱基对。如果有,它将循环添加到结果列表中。
第一个if
是为了避免列出可能被“压缩”得更紧的循环。如条件所示,如果循环太短(少于五个碱基)或末端不匹配,则无法将循环拉得更紧。
结果是一个元组列表,每个可能的循环一个,形式为(start_pos, pair_count, loop_length)
。这意味着pair_count
从碱基编号开始的碱基序列start_pos
后面是一个loop_length
碱基环,然后是反向的互补序列。该序列的反义拷贝从碱基开始start_pos + pair_count + loop_length
。第一个基数是数字 0,而不是 1(我们是这里的程序员)。
一个例子可能会更清楚:print find_loops('GGGGAUUACAGCGUGUAAUCAAUA')
returns [(4, 3, 13), (3, 7, 3)]
,也就是说,它找到了两个循环:
- 在位置 4 处,三个碱基 ,
AUU
围成一个 13 个碱基的环,并与AAU
位置 20 结合;
- 在位置 3,七个碱基,
GAUUACA
包围三个碱基的环,并与UGUAAUC
位置 13 结合。
如果没有第一个if
,该函数还将返回类似 (3, 6, 5) 的循环(即GAUUAC
在位置 3 包含五个碱基的循环并绑定到GUAAUC
位置 14),这与 (3, 7, 3) 相同) 上面,只是没有拉得那么紧。
希望这可以帮助。如果您需要更快的算法,我相信有一个动态编程解决方案可以处理更长的字符串。让我知道,我会考虑的。不过,这不会那么容易理解......