探索貪心演算法:解決最佳化問題的高效策略

最小生成树發表於2024-07-13

貪心演算法是一種在每一步選擇中都採取當前最佳選擇的演算法,以期在整體上達到最優解。它廣泛應用於各種最佳化問題,如最短路徑、最小生成樹、活動選擇等。本文將介紹貪心演算法的基本概念、特點、應用場景及其侷限性。

貪心演算法的基本概念

貪心演算法的核心思想是區域性最優策略,即在每一步選擇中都選擇當前看起來最優的選項,希望透過一系列的區域性最優選擇達到全域性最優。

貪心演算法的特點

  1. 區域性最優選擇:每一步都選擇當前狀態下最優的操作。
  2. 無需回溯:一旦做出選擇,便不會更改。
  3. 逐步構建解決方案:從一個初始解開始,透過區域性最優選擇逐步構建完整解決方案。

貪心演算法的應用場景

1. 活動選擇問題

在活動選擇問題中,給定一組活動及其開始和結束時間,要求選擇儘可能多的互不重疊的活動。

def activity_selection(activities):
    activities.sort(key=lambda x: x[1])  # 按結束時間排序
    selected_activities = [activities[0]]
    
    for i in range(1, len(activities)):
        if activities[i][0] >= selected_activities[-1][1]:
            selected_activities.append(activities[i])
    
    return selected_activities

activities = [(0, 6), (1, 4), (3, 5), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)]
selected = activity_selection(activities)
print("Selected activities:", selected)

2. 揹包問題(分數揹包)

在分數揹包問題中,物品可以部分裝入揹包。目標是選擇物品使得揹包中的總價值最大。

def fractional_knapsack(items, capacity):
    items.sort(key=lambda x: x[1] / x[0], reverse=True)  # 按價值密度排序
    total_value = 0.0
    for weight, value in items:
        if capacity >= weight:
            total_value += value
            capacity -= weight
        else:
            total_value += value * (capacity / weight)
            break
    return total_value

items = [(10, 60), (20, 100), (30, 120)]  # (weight, value)
capacity = 50
max_value = fractional_knapsack(items, capacity)
print("Maximum value in knapsack:", max_value)

3. 最小生成樹(Kruskal 演算法)

在圖論中,最小生成樹是連線所有頂點的權重最小的樹。Kruskal 演算法透過貪心策略選擇最小邊來構建最小生成樹。

class DisjointSet:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            if self.rank[root_u] > self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] < self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(n, edges):
    ds = DisjointSet(n)
    edges.sort(key=lambda x: x[2])
    mst = []
    for u, v, weight in edges:
        if ds.find(u) != ds.find(v):
            ds.union(u, v)
            mst.append((u, v, weight))
    return mst

edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)]
n = 4  # Number of vertices
mst = kruskal(n, edges)
print("Edges in MST:", mst)

貪心演算法的侷限性

雖然貪心演算法在許多問題中表現出色,但它並不適用於所有問題。貪心演算法不能保證所有情況下都能找到全域性最優解。例如,在0-1揹包問題中,貪心演算法可能無法找到最優解。

相關文章