一個動態規劃的簡單例題

動態規劃(Dynamic Programming)

它是電腦中解決最優化問題的一種方法,效率高,速度快。

一般思路:

  • 1、窮舉法/暴力搜索
  • 2、記憶化搜索/剪枝
  • 3、改寫成迭代形式

思想

1.動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題
image

2.但是經分解得到的子問題往往不是互相獨立的。不同子問題的數目常常只有多項式量級。在用分治法求解時,有些子問題被重複計算了許多次。

image

3.如果能夠保存已解決的子問題的答案,而在需要時再找出已求得的答案,就可以避免大量重複計算,從而得到多項式時間演算法。

image

例題

e.g

找出最長的遞增的子序列 ,nums = [1,5,2,4,3]
給了一個無序的數組,要求我們找出其中最長的遞增的子序列,比如:1,2,4就是其中的一個;1,2,3是另外一個。

我們可以將問題簡化:要求這個演算法只返回最長序列的「長度」

利用暴力枚舉/暴力搜索:從「1」出發,最長遞增子序列長度:3

img

演算法實現:

def L( nums , i ):# 我們可以定義一個函數L,這個函數會返回從數組第i個數字開始的最長子序列長度
    if i == len(nums) - 1 : # 取到最後一個數字
        return 1
	for j in range ( i + 1 ,len( nums ) ) : # 檢查i後面的所有數字,將索引記為j
        									#遍歷所有的j
  		if nums [ j ] > nums [ i ] : # 只要這個數比當前數大(也就是說可以構成遞增序列)			
            max_len = max( max_len , L( nums , j ) + 1) # 遞歸的調用函數自身,去計算從j開始的最長子序列長度,然後+1得到目前這個序列的總長度
	return max_len

# 接下來只需要對數組中的每一個數i ,依次調用L函數,然後選出長度最長的那個返回即可
def length_of_LTS(nums):
     return max(
            L(nums , i )for i in range ( len( nums))
	 )
        
# 可以帶入之前的數據進行測試
nums = [1,5,2,4,3]
print(length_of_LTS(nums))

演算法優化:記憶化搜索

再次觀察下方的遍歷樹,我們遍歷子序列1,2,4的時候就已經計算過”從4開始的最大子序列的長度”,後面遍歷1,4的時候又計算了一次。因此演算法存在大量重複的計算。

解決方案的關鍵是我們可以在第一次計算的時候,將結果保留下來。

image

def L( nums , i ):
    
     if i in memo:# 看開頭是否保存過這個答案
         return memo [ i ]# 如果是,直接返回結果
    
     # 否則再去計算答案
     if i == len(nums) - 1: # 取到最後一個數字
         return 1
        
     for j in range ( i + 1 ,len( nums ) ) : 
     	if nums [ j ] > nums [ i ]:
             max_len = max( max_len , L( nums , j ) + 1) # 遍歷所有的j

     memo[ i ] = max_len # 哈希表,記錄下「從i開始最長的子序列長度」
     return max_len

def length_of_LTS(nums):
    return max(
            L(nums , i )for i in range ( len( nums))
)

這裡用到了記憶化搜索,這也就是為什麼大家常說用空間換時間

迭代/非遞歸的實現

從最後的式子可以推至開頭的式子,是不是很像數學歸納法?

img

def length_of_LTS(nums);
     n = len( nums )  # 5
     L = [1 ] * n # initial value: [1,1,1,1,1] 
# 通過兩個循環,外面的循環代表從下往上的依次計算;裡面的循環用於遍歷括弧中的這些數值,運算的結果可以存放到一個數組中,直接叫L
     for i in reversed(range(n)): # i -> 4,3,2,1,0
      	for j in range(i + 1 , n):
          	if nums [ j ] > nums [ i ] :
                L[ i ] = max(L[ i ],L [ j ] + 1)                
	return max(L)

10分鐘徹底搞懂「動態規劃」演算法_嗶哩嗶哩_bilibili

配套影片的鏈接哦