Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

Bellman-Ford 演算法

| , 2 minutes reading.

Bellman-Ford:謹慎的審計師

演算法背後的故事:那個給一切起名字的人

20 世紀 50 年代,理查德·貝爾曼 (Richard Bellman) 在蘭德公司 (RAND Corporation) 工作。那是一個冷戰籠罩的時代,「動態規劃 (Dynamic Programming)」——這一演算法的核心哲學——最初並不是一個純技術名詞。貝爾曼後來承認,他之所以選擇這個名字,是因為她聽起來讓政客們覺得很高級,從而能掩蓋她實際上在進行純數學研究的事實。

當迪傑斯特拉坐在咖啡館裡尋找「最快」路徑時,貝爾曼和 萊斯特·福特 (Lester Ford) 正在尋找「最穩」的路徑。她們意識到,現實世界中的成本並不總是正數。有時候,採取某種行動反而能讓妳獲得資源(即負權重)。她們建立了一個演算法,不只是為了尋找捷徑,更是為了尋找那些可能摧毀宇宙邏輯的「黑洞」——負權環

為什麼需要它?

Dijkstra 演算法很快,但她有一個致命的弱點:她對「負能量」一無所知。

想像一個金融市場:

  • 用貨幣 A 兌換 B,手續費 $5;
  • 用 B 兌換 C,手續費 $3;
  • 用 C 兌換回 A,對方反而返還妳 $10(權重為 -10)。

如果妳一直在這個圓圈裡走下去,理論上妳可以賺到無限多的錢。這就是套利 (Arbitrage)。Dijkstra 會在這個循環裡永遠迷失,而 Bellman-Ford 正是我們派去尋找這種機會——或者警告我們系統已不穩定的審計師。

演算法是如何「思考」的

如果說 Dijkstra 是一個短跑運動員,那麼 Bellman-Ford 就是一個慢條斯理的審計師

她不耍小聰明,也不使用優先佇列來挑選「下一步的最佳選擇」。相反,她信奉**「大數迭代」**的哲學:

  1. 初始的懷疑: 一開始,她假設除了起點 (0) 以外,所有節點都是不可逾越的 (\infty)。
  2. 地毯式清查: 她盯著圖中的每一條邊。對於每一條從 uv 的道路,她都會問:

    「目前到達 u 的路徑,加上這條路,會不會比我之前記錄的到達 v 的路更好?」

  3. 極致的耐心: 她會將這種對所有邊的全面清查完整地執行 V-1 次(V 是節點總數)。為什麼要這麼多次?因為在不包含環路的情況下,最長的路徑最多也只有 V-1 條邊。
  4. 最後的陷阱: 在清查結束後,她還會進行最後一次掃描。如果即便在 V-1 輪之後,她還能找到更短的路徑,她就會尖叫:

    「檢測到異常!這裡存在一個吞噬邏輯的負權環。」

這種將大問題拆解為多次完全相同的微小步驟的策略,正是動態規劃 (Dynamic Programming) 的精髓。

工程決策:何時選擇這條「慢路」

Bellman-Ford 比 Dijkstra 慢得多(複雜度 O(VE)O(V \cdot E))。在生產環境中,我們只有在以下情況才會選她:

  • 存在負成本: 比如返現獎勵、電動汽車的動能回收、或者是金融套利模型。
  • 分散式路由: 網際網路早期的 RIP 協定 使用了她的變體,因為在伺服器只知道鄰居資訊的情況下,這種演算法更容易實作。

當妳更看重複雜環境下的可靠性而非單純的速度時,她就是妳的首選。

實作參考 (Python)

def bellman_ford(graph, edges, start):
    # 1. 初始化:審計師從一張白紙開始
    distances = {node: float('inf') for node in graph}
    distances[start] = 0

    # 2. 迭代 V-1 次
    # 我們一次又一次地檢查每一條邊,讓真相在網絡中傳播
    for _ in range(len(graph) - 1):
        for u, v, weight in edges:
            if distances[u] != float('inf') and distances[u] + weight < distances[v]:
                distances[v] = distances[u] + weight

    # 3. 終極掃描:尋找「無限刷錢」的漏洞 (負權環)
    for u, v, weight in edges:
        if distances[u] != float('inf') and distances[u] + weight < distances[v]:
            raise ValueError("檢測到負權環!系統處於不穩定狀態。")

    return distances

# 使用範例:
# nodes = ['A', 'B', 'C']
# edges = [('A', 'B', 5), ('B', 'C', 3), ('C', 'A', -10)]
# try:
#     print(bellman_ford(nodes, edges, 'A'))
# except ValueError as e:
#     print(e)

小結

Bellman-Ford 提醒我們,在工程世界裡,速度並不是一切。有時候世界是混亂的,充滿了負成本和不穩定的循環。在那些時刻,我們需要的不是一個短跑運動員,而是一個有耐心的審計師,願意為了真相把帳本再翻一遍。