1. 程式人生 > >演算法優化:動態規劃加速,貨物運輸問題,四邊形不等式, 從O(n^2)到O(n^3)

演算法優化:動態規劃加速,貨物運輸問題,四邊形不等式, 從O(n^2)到O(n^3)

貨物運輸問題

在這裡插入圖片描述

遞迴方程為:

在這裡插入圖片描述

更為一般形式的遞迴方程

在這裡插入圖片描述

看起來是不是像可以使用分治的策略實現,但是min裡面子問題太多了,只能使用動態規劃的招了。

i,j是什麼含義了?動態規劃裡i,j都是指的是問題規模,對應到貨物運輸問題指的是什麼了?我們從數學上理解i,j是指陣列arr = [1,2,3,4,5,6,7,8]兩邊的標號,或者子問題對應的起始和終止標號,類似與快排裡面的陣列的標號,實際問題抽象為陣列之後,就比較容易理解,在實際問題裡面i,j就非常不好理解,i,j是指的是子問題的起始標號和終止標號。

不是動態規劃裡[i,j]指的是問題規模嗎?這裡怎麼是子問題的起始標號和終止標號,起始標號和終止標號實際上對應的就是問題的規模,j-i就是問題規模,也就是使用了二維表示了一維的規模,快排裡也可以不適用quick_sort(arr,p,q),直接使用quick_sort(left_or_right_arr)內部直接使用的left_or_right_arr的0和end。要想原地操作就需要藉助於p,q。

假如一個實際問題已經抽象成了一個數組問題,就可以直接從陣列的思路考慮,暫時忘記實際的問題,從建立的模型考慮,這樣就可以回到熟悉的套路上來。

現在考慮如何實現:

m[i,j] = min{m[i,k-1] + m[k,j] } + w[i,j] i<k<=j;i ==j ,m[i,j] =0

程式碼實現如下,這種動態規劃,根據約束調價i<j,他掃描是沿著對角線方向掃描,對角線從左往右平移,直到掃描到右上角最後一個點,就是最終的解,時間複雜度為O(n^3)

# -*- coding: utf-8 -*-
import numpy as np
# 任意點到第一個點的和
def sum_(arr,n): b = [0]*(n+1) for i in range(1,n+1): b[i] = b[i-1] + arr[i-1] return b # 任意兩點之間的陣列和 def weight_i_j(b,i,j): return b[j+1]-b[i] # m[i,j] = min{m[i,k-1] + m[k,j] } + w[i,j] i<k<=j,0<i<j<n+1 # i ==j ,m[i,j] =0 def DynamicProgramming
(arr): n = len(arr) b = sum_(arr,n) print(b) # 初始化,這個問題初始化為i==j時為0,也就是對角線上均為0 m = np.zeros((n,n),int) solution = np.full((n,n),-1) # 掃描的時候注意了,這種型別的問題,掃描順序不是從左到右,從上到下 # 掃描的基準是j-i的間距,沿著對角線往右上掃描,這裡的i就是為j-i的長度,掃描的最後就是 # m[0,n-1],這個就是最後的解 # i <j 所以間距為從1 到 n-1 for r in range(1,n): # 沿著對角線往下走 for i in range(n-r): j = i + r # 首先取第一個k = i+1 m[i][j] = m[i][i] + m[i+1][j] # 用於解的追蹤 solution[i][j] = i # 比較求最小的 for k in range(i+2,j+1): temp = m[i][k-1] + m[k][j] if temp < m[i][j]: m[i][j] = temp solution[i][j] = k-1 # min{m[i,k-1] + m[k,j] } + w[i,j] m[i][j] += weight_i_j(b,i,j) return m,solution arr = [1000,22,13,4,5000,86,57,18] print(arr) m,s = DynamicProgramming(arr) print(m) print(end='\n') print(s) runfile('D:/share/test/dp.py', wdir='D:/share/test') [1000, 22, 13, 4, 5000, 86, 57, 18] [0, 1000, 1022, 1035, 1039, 6039, 6125, 6182, 6200] [[ 0 1022 1070 1095 7134 12306 12563 12692] [ 0 0 35 56 5095 10220 10420 10531] [ 0 0 0 17 5034 10137 10337 10448] [ 0 0 0 0 5004 10094 10294 10405] [ 0 0 0 0 0 5086 5286 5397] [ 0 0 0 0 0 0 143 236] [ 0 0 0 0 0 0 0 75] [ 0 0 0 0 0 0 0 0]] [[-1 0 0 0 3 3 3 3] [-1 -1 1 1 3 4 4 4] [-1 -1 -1 2 3 4 4 4] [-1 -1 -1 -1 3 4 4 4] [-1 -1 -1 -1 -1 4 4 4] [-1 -1 -1 -1 -1 -1 5 5] [-1 -1 -1 -1 -1 -1 -1 6] [-1 -1 -1 -1 -1 -1 -1 -1]]

四邊形不等式

在這裡插入圖片描述

結論

在這裡插入圖片描述
在這裡插入圖片描述

回到實際的問題

在這裡插入圖片描述

程式碼實現如下,實現的時候,只要把原來的i<k<=j,修改成s[i,j-1]<=k<=s[i+1,j]就可以了,時間複雜度為O(n^2)

def DynamicProgrammingSpeed(arr):
    n = len(arr)
    b = sum_(arr,n)
    print(b)
    
    # 初始化,這個問題初始化為i==j時為0,也就是對角線上均為0
    m = np.zeros((n,n),int)
    solution = np.full((n,n),-1)
    
    # 掃描的時候注意了,這種型別的問題,掃描順序不是從左到右,從上到下
    # 掃描的基準是j-i的間距,沿著對角線往右上掃描,這裡的i就是為j-i的長度,掃描的最後就是
    # m[0,n-1],這個就是最後的解
    # i <j 所以間距為從1 到 n-1
    for r in range(1,n):
        # 沿著對角線往下走
        for i in range(n-r):
            j = i + r        
            
            # 利用四邊形不等式,把範圍縮小為s[i][j-1]到s[i+1][j]
            i1 = s[i][j-1]
            j1 = s[i+1][j]
            # 首先取第一個k = i1
            m[i][j] = m[i][i1] + m[i1+1][j]
            # 用於解的追蹤
            solution[i][j] = i1
            
            # 比較求最小的
            for k in range(i1+1,j1+1):
                temp = m[i][k-1] + m[k][j]
                
                if temp < m[i][j]:
                    m[i][j] = temp
                    solution[i][j] = k-1
            # min{m[i,k-1] + m[k,j] } + w[i,j]       
            m[i][j] += weight_i_j(b,i,j)
    
    return m,solution    
    
    

arr = [1000,22,13,4,5000,86,57,18]
print(arr)
m,s = DynamicProgrammingSpeed(arr)
print(m)
print(end='\n')
print(s)

[1000, 22, 13, 4, 5000, 86, 57, 18]
[0, 1000, 1022, 1035, 1039, 6039, 6125, 6182, 6200]
[[    0  1022  1035  1078 12078 12250 12403 12600]
 [    0     0    35    39 10056 10250 10364 15417]
 [    0     0     0    17  5017 10193 10320 10356]
 [    0     0     0     0  5004  5090 10290 10330]
 [    0     0     0     0     0  5086  5143  5322]
 [    0     0     0     0     0     0   143   161]
 [    0     0     0     0     0     0     0    75]
 [    0     0     0     0     0     0     0     0]]

[[-1 -1 -1  0  1  2  3  3]
 [-1 -1 -1 -1  1  2  3  4]
 [-1 -1 -1 -1 -1  2  3  4]
 [-1 -1 -1 -1 -1 -1  3  4]
 [-1 -1 -1 -1 -1 -1 -1  4]
 [-1 -1 -1 -1 -1 -1 -1 -1]
 [-1 -1 -1 -1 -1 -1 -1 -1]
 [-1 -1 -1 -1 -1 -1 -1 -1]]