簡體   English   中英

動態規划:通過矩陣的最小成本路徑——記憶?

[英]Dynamic Programming: Smallest cost path through matrix -- memoization?

有沒有一種簡單的方法來記憶結果? 我的動態編程解決方案可能會使用相同的參數多次調用相同的 function。

我認為記憶會增加速度。 但是,我不確定最好的方法是什么。 這是原始的 function,雖然它沒有被記住,但它可以工作:

def dim(M):
    rows = len(M)
    cols = len(M[0])
    return rows, cols

def minimumCostPath(matrix, i=0, j=0, total=0):
    r,c = dim(matrix)
    if i+1 < r and j+1 < c:
        down = matrix[i+1][j]
        right = matrix[i][j+1]
        return min(minimumCostPath(matrix, i+1, j, total+down),
                    minimumCostPath(matrix, i, j+1, total+right))
    elif i+1 < r:
        right = matrix[i+1][j]
        return minimumCostPath(matrix, i+1, j, total+right)
    elif j+1 < c: 
        down = matrix[i][j+1]
        return minimumCostPath(matrix, i, j+1, total+down)
    else:
        return total + matrix[0][0]       

test = [ [23,70,54], 
         [86,5,13], 
         [86,62,77], 
         [60,37,32], 
         [88,58,98] ]
total = minimumCostPath(test)
>>>
318

下面是我用零矩陣(嵌套列表)來記憶這個 function 的嘗試。

def solution(matrix):
  cache = [[0 for j in range(len(matrix[0]))] for i in range(len(matrix))] 
  return helper(matrix, cache, i=0, j=0, total=0) 

def helper(matrix, cache, i=0, j=0, total=0):
    r,c = dim(matrix)
    if i+1 < r and j+1 < c:
        down = matrix[i+1][j]
        right = matrix[i][j+1]
        
        if cache[i+1][j] > 0:
          go_down = cache[i+1][j] + down
        else:
          go_down = helper(matrix, cache, i+1, j, total+down)
          cache[i+1][j] = go_down
        
        if cache[i][j+1] > 0 :
          go_right = cache[i][j+1] + right
        else:
          go_right = helper(matrix, cache, i, j+1, total+right)
          cache[i][j+1] = go_right 
        
        return min(go_down, go_right)
    elif i+1 < r:
        down = matrix[i+1][j]
        if cache[i+1][j] > 0:
          go_down = cache[i+1][j] + down
        else:
          go_down = helper(matrix, cache, i+1, j, total+down)
          cache[i+1][j] = go_down  
          return go_down     
    
    elif j+1 < c: 
        right = matrix[i][j+1]
        if cache[i][j+1] > 0 :
          go_right = cache[i][j+1] + right
        else:
          go_right = helper(matrix, cache, i, j+1, total+right)
          cache[i][j+1] = go_right         
        return go_right
    
    else:
        return total + matrix[0][0]


solution(test)

兩個問題。

  1. 我收到一個錯誤, TypeError: '<' not supported between instances of 'NoneType' and 'int'第 23 行將 go_right 或 go_down 評估為 None,這很奇怪。
  2. 它不是很簡潔或漂亮。 也許更好的助手 function 會簡化代碼。

最后,我知道有一種自下而上的方法,它不使用遞歸,而是迭代地填充表格單元格。 在這一點上,我想知道遞歸解決方案如何利用記憶,而不是從頭開始實現自下而上。

首先,您的錯誤:在其中一個分支中, return go_down縮進太遠,因此go_down的非遞歸計算不會返回該值; 相反,它會從 function 的末尾脫落並返回隱式None

就記憶化而言, functools中有cachelru_cache裝飾器。 上次我使用它(大約 5 年前和許多版本)它有點慢,只對外部數據(磁盤或網絡)真正有用; 您必須衡量它是否對您而言令人滿意。 從那時起,它可能已經改善了很多。

如果您確實需要手動實現緩存(如果functools.cache裝飾器被證明太慢),那么緩存分離的模式可能會更好,以避免混合關注點:

minimumCostPath_cache = {}

def minimumCostPath(matrix, i=0, j=0):
    try:
        return minimumCostPath_cache[i, j]
    except KeyError:
        result = minimumCostPath_cache[i, j] = minimumCostPath_raw(matrix, i, j)
        return result

def minimumCostPath_raw(matrix, i=0, j=0):
   ...

為避免全局變量和具有不同矩陣的調用相互干擾,您可以在 class 中執行此操作:

class MinimumCostPath:
    def __init__(self, matrix):
        self.cache = {}
        self.matrix = matrix

    def calculate(self, i=0, j=0):
        try:
            return self.cache[i, j]
        except KeyError:
            result = self.cache[i, j] = self.calculate_uncached(i, j)
            return result

    def calculate_uncached(self, i=0, j=0):
        ...

如果您有 Python 3.9,請了解@cache裝飾器以實現無憂記憶

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

>>> factorial(10)      # no previously cached result, makes 11 recursive calls
3628800
>>> factorial(5)       # just looks up cached value result
120
>>> factorial(12)      # makes two new recursive calls, the other 10 are cached
479001600

來源: https://docs.python.org/3/library/functools.html

from functools import lru_cache
@lru_cache(maxsize=None, typed=False)
def minimumCostPath(matrix, i=0, j=0):
    r,c = len(M), len(M[0])
    if i+1 < r and j+1 < c:
        down = matrix[i+1][j]
        right = matrix[i][j+1]
        return min(minimumCostPath(matrix, i+1, j) + down,
                    minimumCostPath(matrix, i, j+1) + right)
    elif i+1 < r:
        right = matrix[i+1][j]
        return minimumCostPath(matrix,i+1, j) + right
    elif j+1 < c: 
        down = matrix[i][j+1]
        return minimumCostPath(matrix,i, j+1) + down
    else:
        return matrix[0][0]

將總參數移出並使用 lru_cache 保存以前的 function 調用。 它有 8 次安打,15 次未命中,目前尺寸為 15。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM