动态规划
动态规划(dynamic programming), 是一种求解多阶段决策过程最优化问题的算法。 核心思想就是: (1),将一个大问题拆解成一堆子问题,(2),求解出子问题的解,存储起来当再次需要求解此问题时候可以被重复利用,避免重复的计算过程。 能够使用DP必须满足下面三个特征:
(1), 最优子结构性质。一个问题的最优解包含其子问题的最优解。
(2), 重叠子问题性质。在求解子问题的过程中,会有大量的子问题的是重复的,可以将子问题的结果存储,后续直接查询,避免重复计算。 (3), 无后效性。子问题的解只与之前的阶段有关,与后面的阶段无关。
1 算法解释
对于序列比对问题,我们想知道两个序列之间的存在的差异,首先我们想了解全局比对的差异。 对于全局比对,前人开发出Needleman-Wunsch算法,该算法是基于动态规划的全局比对算法。
\[ F(i, j)=max \left\{
\begin{aligned}
F(i-1, j-1) + s(x_i, y_j) \\
F(i-1, j) + s(x_i, -) \\
F(i, j-1) + s(-, y_j)
\end{aligned}
\right.
\]
\(F(i-1, j-1) + s(x_i, y_j)\) 前半部分\(F(i-1, j-1)\)表示前面\(i-1\)和\(j-1\)比对计算完成的情况下, 比对两条序列的当前碱基\(x_i\)和\(y_j\), \(s(x_i, y_j)\)表示当前的替换分数。
\(F(i-1, j) + s(x_i, -)\) 前半部分\(F(i-1, j)\)表示前面\(i-1\)和\(j\)比对计算完成的情况下, 比对\(x\)序列的当前碱基\(x_i\)和\(y\)序列空白\(-\), \(s(x_i, -)\)表示\(x\)序列当前碱基和gap的替换分数。
\(F(i, j-1) + s(-, y_j)\) 前半部分\(F(i, j-1)\)表示前面\(i\)和\(j-1\)比对计算完成的情况下,
比对\(y\)序列的当前碱基\(y_j\)和\(x\)序列空白\(-\), \(s(-, y_j)\)表示\(y\)序列当前碱基和gap的替换分数。
2 罚分矩阵
常见的罚分矩阵有几种:
- 等价矩阵:碱基相同为1,不同为0
- 转换-颠换矩阵:嘌呤(AG)之间为转换,嘧啶(TC)之间为转换,嘧啶和嘌呤之间为颠换
- BLAST矩阵:碱基相同为5,碱基不同为-4
NCBI Blast matrices
这里实现的代码采用自定的一个矩阵,具体如下:
对角线为自身比对上的分数0,即match。
嘌呤A/G之间比对分数为2,即mismatch。
嘧啶T/C之间比对分数为2,即mismatch。
嘧啶(T/C)和嘌呤(A/G)之间的比对分数为4, 即mismatch。
空白罚分为8,即gap。
Gap一般分2种:
Gap opening(只有出现一个就罚分),
Gap extension(在一空位的扩大,拓展空位罚分,长度越长,罚分越多)
1 2 3 4 5 6 7 8
|
A T G C - A 0 4 2 4 8 T 4 0 4 2 8 G 2 4 0 4 8 C 4 2 4 0 8 - 8 8 8 8 8
|
3 全局比对python实现
1 2 3 4 5 6 7
| alphabet = ['A', 'T', 'G', 'C'] score = [[0, 4, 2, 4, 8], [4, 0, 4, 2, 8], [2, 4, 0, 4, 8], [4, 2, 4, 0, 8], [8, 8, 8, 8, 8]]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import numpy as np
def globalAlignment(x, y): """ Needleman-Wunsch algorithm x : (str) sequence a y : (str) sequence b """ D = [] for i in range(len(x)+1): D.append([0] * (len(y)+1)) for i in range(1, len(x)+1): D[i][0] = D[i-1][0] + score[alphabet.index(x[i-1])][-1] for j in range(1, len(y)+1): D[0][j] = D[0][j-1] + score[-1][alphabet.index(y[j-1])] for i in range(1, len(x)+1): for j in range(1, len(y)+1): distHor = D[i][j-1] + score[-1][alphabet.index(y[j-1])] distVer = D[i-1][j] + score[alphabet.index(x[i-1])][-1] distDiag = D[i-1][j-1] + score[alphabet.index(x[i-1])][alphabet.index(y[j-1])] D[i][j] = min(distHor, distVer, distDiag) return D, D[-1][-1]
|
1 2 3 4 5 6 7 8
| x = 'TATGTCATGC' y = 'AATGTCATGC' D, value = globalAlignment(x,y) import numpy as np d = np.array(D) np.diagonal(d) print(value) print(D)
|
4 局部比对
在生物学序列比对中,由于内含子或者motif的因素的存在,我们常常需要算出局部最佳的比对情况, 类似最大子串问题,因此需要局部比对,前人实现了smith-waterman算法来解决这个问题。 和全局比对相比,局部比对在算法上只有一点不同。
\[ F(i, j)= max \left\{
\begin{aligned}
F(i-1, j-1) + s(x_i, y_j) \\
F(i-1, j) + s(x_i, -) \\
F(i, j-1) + s(-, y_j) \\
0
\end{aligned}
\right.
\]
5 局部比对python实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import numpy
def costFunction(xc, yc): ''' 自定义一个罚分矩阵 2 to match, -6 to gap, -4 to mismatch ''' if xc == yc: return 2 if xc == '-' or yc == '-': return -6 return -4
def localAlignment(x, y, s): ''' Smith-Waterman algorithm Calculate local alignment values of sequences x and y using dynamic programming. Return maximal local alignment value. ''' D = numpy.zeros((len(x)+1, len(y)+1), dtype=int) for i in range(1, len(x)+1): for j in range(1, len(y)+1): D[i, j] = max(D[i-1, j-1] + s(x[i-1], y[j-1]), D[i-1, j ] + s(x[i-1], '-'), D[i , j-1] + s('-', y[j-1]), 0) argmax = numpy.where(D == D.max()) return D, int(D[argmax])
x, y = 'GGTATGCTGGCGCTA', 'TATATGCGGCGTTT' D, best = localAlignment(x, y, exampleCost) print(D) print("Best score=%d, in cell %s" % (best, numpy.unravel_index(numpy.argmax(D), D.shape)))
|