已發布·持續改進
Algorithm
分治法把問題拆成更小的子問題,遞迴求解後再合併結果,是合併排序、快速冪和快速選擇等演算法的基礎。
分治法(divide and conquer)是一種演算法設計方法:把問題拆成規模更小的同類子問題(分解),遞迴地求解這些子問題(解決),再用它們的結果構造原問題的答案(合併)。合併排序、二分搜尋、快速冪、快速選擇、Karatsuba 乘法和最近點對演算法都遵循這個模式。
分治法之所以重要,是因為它常常能把 O(n^2) 的暴力解法降到 O(n log n) 甚至 O(log n)。像計算逆序對那樣,在合併階段有效率地計算跨越分界線的資訊,是面試與程式競賽中的常見思路;同樣的想法也用在排序函式庫、大整數乘法、FFT 以及密碼學中的模冪運算等實際系統裡。
學習時,先手動追蹤合併排序,熟悉分解、解決、合併的流程,再練習寫出 T(n) = aT(n/b) + f(n) 這樣的遞迴關係式,並用主定理求出時間複雜度。接著親手實作逆序對計數、快速冪和快速選擇,並學會判斷:當子問題彼此重疊時,應改用動態規劃。
分解輸入、遞迴求解各部分、合併答案。基本情況讓遞迴停止,合併的成本通常決定整體執行時間。
把執行時間寫成 T(n) = aT(n/b) + f(n),再將 f(n) 與 n^(log_b a) 比較,就能直接得到合併排序 O(n log n) 這樣的結果。
在合併階段以線性時間處理橫跨兩半的情況,例如逆序對計數與最大子陣列和,是分治法的核心思路。
像二分搜尋、快速冪和快速選擇那樣只保留一個子問題,就能得到 O(log n) 或期望 O(n) 的演算法。
sort_count 以合併排序對串列排序,每當右半部的元素先被取出時,就加上左側仍在等待的元素個數,藉此在 O(n log n) 內計算逆序對。power 是快速冪:不斷把指數減半並平方,只需要 O(log e) 次乘法。執行 python divide_and_conquer.py 會輸出排序後的串列、逆序對數 14,以及 3^200 除以 1,000,000,007 的餘數。
divide_and_conquer.py
def sort_count(a):
"""Return (sorted list, number of inversions) using merge sort."""
if len(a) <= 1:
return list(a), 0
mid = len(a) // 2
left, x = sort_count(a[:mid])
right, y = sort_count(a[mid:])
merged, i, j, cross = [], 0, 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merged.append(left[i])
i += 1
else:
merged.append(right[j])
j += 1
cross += len(left) - i # right[j] is smaller than every left[i:]
merged += left[i:] + right[j:]
return merged, x + y + cross
def power(base, exp, mod):
"""base ** exp % mod with O(log exp) multiplications."""
if exp == 0:
return 1 % mod
half = power(base, exp // 2, mod)
result = half * half % mod
return result * base % mod if exp % 2 else result
if __name__ == "__main__":
print(sort_count([5, 2, 4, 7, 1, 3, 2, 6])) # ([1, 2, 2, 3, 4, 5, 6, 7], 14)
print(power(3, 200, 1_000_000_007)) # 136318165
python divide_and_conquer.py共六章,帶你從安裝一步步認識 分治法 的核心概念。
在這裡提問、分享經驗,交流關於 分治法 的看法。
還沒有討論。來發起第一則吧。
0 則留言
登入 · 登入後即可留言。
來留下第一則留言吧。