动态规划代码实现:Fibonacci数列、找到所有的最长公共子序列LCS

fibonacci数列

  • 黄金分割数列/兔子数列(因以兔子繁殖为例子引入)

    • 假设一对兔子(一雄一雌)出生后第三个月开始繁殖,每个月都可以生一对兔子,而新生的兔子出生后第三个月也开始繁殖。问n个月后,有多少对兔子?

      斐波那契通过推理得到了以下递归关系:

      f(0) = 0 f(1) = 1 f(n) = f(n-1) + f(n-2) (n≥2)

      其中,f(n)表示第n个月的兔子对数。

  • 从第三项开始,每一项都是前两项的和

    • F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)
    • 线性递推数列

img

代码实现

  • 递归实现:
def Fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return Fibonacci(n - 1) + Fibonacci(n - 2)
  • 迭代实现:
def FasterFibonacci(n):
    F = [0, 1]
    for i in range(2, n + 1):
        F.append(F[i - 1] + F[i - 2])
    return F[n]

递归有着大量的重复运算,使用迭代运算会节省许多时间。

  • 对比递归与迭代的时间:
start = time.time()
Fibonacci(30) # 因为n大时递归太慢了,所以用小n递归和大n迭代也能比较
end = time.time()
print('n=30递归:'+ str(end - start))

start = time.time()
FasterFibonacci(1000)
end = time.time()
print('n=1000迭代:'+ str(end - start))

# 输出结果:
# n=30递归:0.4313187599182129
# n=1000迭代:0.0005943775177001953

最长公共子序列LCS

参考:动态规划 最长公共子序列 过程图解_Running07的博客-CSDN博客

子序列sequenceVS子串substring

子序列可不连续,子串则必须连续

img

求出LCS

img

图中的空白格子c[i,j]的定义:记录的LCS的长度值

  • 横竖(i,j)对应的两个元素相等,该格子的值 = c[i-1,j-1] + 1
  • 如果不等,取c[i-1,j] 和 c[i,j-1]的最大值

img

img

S1的元素3=S2的元素3,所以 c[2,1] = c[1,0] + 1

img

S1的元素3 != S2的元素5,c[2,2] =max(c[1,2],c[2,1]),取图中两个黄色的最大值

img

依次填写直到最后一行

依图构造LCS——溯源

  • +1得来的:(作特殊标记)溯源——左上角元素
  • 非+1所得的:从其左方or上方的元素溯源

eg:

c[8][9] = 5,且S1[8] != S2[9],所以倒推回去,c[8][9]的值来源于c[8][8]的值(因为c[8][8] > c[7][9])。
c[8][8] = 5, 且S1[8] = S2[8], 所以倒推回去,c[8][8]的值来源于 c[7][7]。

如图所示:请添加图片描述

即LCS ={3,5,7,7,8}或{3,4,6,7,8}

代码实现

# 从后往前溯源,每个相等的元素入栈,找到一个完整的LCS后依次出栈
# 此时可找到不同的LCS
stack = []
# 答案用集合存储,因为集合本身不可重复,可去重
ans = set()


# LCS:最长公共子序列
def LCS(X, Y, i, j, flag):
    if flag[i][j] == 0:
        # print(stack)
        # 因为入栈顺序从后到前,因此变为字符串时需倒序
        s = ''.join(stack[len(stack) - 1 - i] for i in range(len(stack)))
        ans.add(s)
        return
    if flag[i][j] == 'p':
        stack.append(X[i - 1])
        LCS(X, Y, i - 1, j - 1, flag)
        stack.pop(-1)
    elif flag[i][j] == 'l':
        LCS(X, Y, i, j - 1, flag)
    elif flag[i][j] == 'u':
        LCS(X, Y, i - 1, j, flag)
    else:
        LCS(X, Y, i - 1, j, flag)
        LCS(X, Y, i, j - 1, flag)

    return ans


# length为上面所画的表格
# flag表示该长度溯源
def LCSlength(X, Y):
    lenth = [[0 for col in range(len(X) + 1)] for row in range(len(Y) + 1)]
    flag = [[0 for col in range(len(X) + 1)] for row in range(len(Y) + 1)]
    for i in range(len(X)):
        for j in range(len(Y)):
            if X[i] == Y[j]:
                lenth[i + 1][j + 1] = lenth[i][j] + 1
                flag[i + 1][j + 1] = 'p'  # plus,即溯源左上角
            elif lenth[i + 1][j] > lenth[i][j + 1]:
                lenth[i + 1][j + 1] = lenth[i + 1][j]
                flag[i + 1][j + 1] = 'l'  # left,即溯源左侧元素
            elif lenth[i + 1][j] < lenth[i][j + 1]:
                lenth[i + 1][j + 1] = lenth[i][j + 1]
                flag[i + 1][j + 1] = 'u'  # up,即溯源右侧元素
            else:
                lenth[i + 1][j + 1] = lenth[i][j + 1]
                flag[i + 1][j + 1] = 'b'  # both,左上皆可,可通过不同路径找到不同的LCS
            if lenth[i + 1][j + 1] == 0: flag[i + 1][j + 1] = 0
    return flag

测试:

X = 'ACCGGTC'
Y = 'GTCGTTC'
X = list(X)
Y = list(Y)
flag = LCSlength(X, Y)
print(LCS(X, Y, len(X), len(Y), flag))

# 输出结果:
# {'GGTC', 'CGTC'}

表格表示:
请添加图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值