赞
踩
数据的算法与结构是每个程序员必须熬过的基础之一,相信大家在解析问题的时候总会迸发灵感想出自己的算法。下面结合算法中的经典问题:矩阵路径和最小,提出问题不同的求解方法,方便初学者更好的理解算法结构以及分析问题的思路。
问题:给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有路径和中最小的路径和。
方法1:递归寻找所有路径和 (左、上两个方向)
对于这种重复进行操作判断的问题,我们最容易想到的就是递归了,暴力递归总是我们最好的朋友。
import numpy as np def f(v, i, j): #到达原点终止 if i == 0 and j == 0: return v[0][0] #到达最上端,不再执行上走 elif i == 0: return f(v, i, j - 1) + v[i][j] #j-1表示向左走 #到达最左端,不再执行左走 elif j == 0: return f(v, i - 1, j) + v[i][j] #i-1表示向上走; #以上为终止条件,else中的是递归的链条 else: #比较左走还是上走的产生的路径和小,进行累加 return min(f(v, i - 1, j), f(v, i, j - 1)) + v[i][j] v = np.array([[1, 3, 5, 9], [8, 1, 3, 4], [5, 0, 6, 1], [8, 8, 4, 0]]) print(f(v, 3, 3))
既然是从左上角(A点)抵达右下角(B点)的路径和,那么我们就可以巧妙地利用逆向的思维,将问题转化未由右下角退回至左上角,可以减少我们定义变量的数量,以及分析问题的复杂程度。
问题就变为 :f(v, i, j)=f(v, i, j)+v [i] [j]
这里的f(v, i, j)就是我们存储的上一步的路径和;v [i] [j]是当前步骤的数字
因此递归的要素:
终止条件:到达A点(i=0,j=0)
链条:判断左走的路径和小,还是右走的路径和小,累加数字
我们再对问题进行一次回顾,每走一步即判断下一步是左走还是上走比较好,由于递归的层层嵌套,这里已经将所有路径的可能都进行了比较取优,时间复杂度Θ(nlgn),这怎么能够满足我们对知识的探索呢?那怎么减少计算的次数呢?
因此再提出另一种方法:动态规划。
'''方法2:动态规划(下、右两个方向)''' def minSum(list1): l1=len(list1) if l1<2: return list1 list2=list1 for i in range(1,l1): #设定了一个初始值矩阵,指定0行0列数字为第一个数字进行向右或是向下产生的路径和, list2[0][i]=list1[0][i-1]+list1[0][i] list2[i][0]=list1[i-1][0]+list1[i][0] for i in range(1,l1): for j in range(1,l1): sum1=list2[i][j-1]+list1[i][j] #对于矩阵中的每一个元素进行一个判别,从第上一个元素到达该位置,是执行向下走 j-1 的数值小还是向右走 i-1 的路径和数值小 sum2=list2[i-1][j]+list1[i][j] if sum1<sum2: list2[i][j]=sum1 #判别成功后,将路径和小的那个路径,写在最后的元素的位置上替代该元素。之后进行循环判别各个路径加上哪个新元素的路径和小 else: list2[i][j]=sum2 return list2[l1-1][l1-1] print(minSum(v))
动态规划的思路是,为路径和再生成一个矩阵list2,该矩阵的维度与原矩阵相同,矩阵的每个位置保存从原点到达该位置的路径和(该路径和需判断是否向右走的产生小,还是向下走产生的小),即每走一步更新一次矩阵,当到达终点是,矩阵的最后一个元素即为最小路径和。
动态规划的步骤更为清晰,因此直接遍历元素即可达到效果。因此时间复杂度O(M×N)远小于递归。
这里我们进行一个简单的验证:
两个函数循环运行1000次
import datetime import time t1 = datetime.datetime.now().microsecond t3 = time.mktime(datetime.datetime.now().timetuple()) for m in range(1000): f(v, 3, 3) t2 = datetime.datetime.now().microsecond t4 = time.mktime(datetime.datetime.now().timetuple()) strTime = '递归 use:%dms' % ((t4 - t3) * 1000 + (t2 - t1) / 1000) print(strTime) '''''' t1 = datetime.datetime.now().microsecond t3 = time.mktime(datetime.datetime.now().timetuple()) for m in range(1000): minSum(v) t2 = datetime.datetime.now().microsecond t4 = time.mktime(datetime.datetime.now().timetuple()) strTime = '动态规划 use:%dms' % ((t4 - t3) * 1000 + (t2 - t1) / 1000) print(strTime)
输出结果为:
12
12
递归 use:34ms
动态规划 use:21ms
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。