📜  贪婪算法(一般结构和应用)(1)

📅  最后修改于: 2023-12-03 15:41:49.535000             🧑  作者: Mango

贪婪算法(Greedy Algorithm)

一般结构

贪婪算法是一种贪心思想,即每一步选择当前局部最优解,最终得到的就是全局最优解。它的一般结构如下:

  1. 初始化:选择最基本的信息,对问题进行初步处理。

  2. 重复执行以下步骤直到满足目标:

    1. 选择当前状态下的局部最优解。

    2. 根据所选解,修改问题的约束条件。

    3. 判断问题是否已经满足终止条件,如果满足,则停止算法。

应用

贪婪算法主要适用于满足“无后效性”的问题,即某个状态以后的过程不会影响以前做出的决策。以下是几个贪婪算法的应用示例。

最小生成树

最小生成树(Minimum Spanning Tree)的问题是:在一个带权连通图中找一棵生成树,使得树的权值之和最小。

Kruskal算法是一种贪心算法,它的主要思想是从小到大选择边,加入到生成树中,如果加入该边会形成环,则不加入该边。

代码示例:

def Kruskal(Graph):
    parent = dict()
    rank = dict()
  
    def make_set(vertex):
        parent[vertex] = vertex
        rank[vertex] = 0
  
    def find(vertex):
        if parent[vertex] != vertex:
            parent[vertex] = find(parent[vertex])
        return parent[vertex]
  
    def union(vertex1, vertex2):
        root1 = find(vertex1)
        root2 = find(vertex2)
        if root1 != root2:
            if rank[root1] > rank[root2]:
                parent[root2] = root1
            else:
                parent[root1] = root2
                if rank[root1] == rank[root2]: rank[root2] += 1
  
    for vertex in Graph['vertices']:
        make_set(vertex)
  
    MST = set()
    edges = list(Graph['edges'])
    edges.sort()
    for edge in edges:
        weight, vertex1, vertex2 = edge
        if find(vertex1) != find(vertex2):
            union(vertex1, vertex2)
            MST.add(edge)
  
    return MST
背包问题

背包问题(Knapsack Problem)是指给定一个背包容量以及物品列表,每个物品都有一个重量和一个价值,要求从中选出一些物品放入背包中,使得放入的物品重量之和不超过背包容量,同时让背包中物品的价值最大。

贪心算法在背包问题中的应用是:按照单位价值排序,将价值最大的放入背包中,直至背包不能再放任何物品为止。

代码示例:

def knapsack(weights, values, capacity):
    n = len(weights)
    ratios = [(values[i] / weights[i], weights[i], values[i]) for i in range(n)]
    ratios.sort(reverse=True)
    total = 0
  
    for r in ratios:
        if capacity == 0:
            break
        if r[1] <= capacity:
            total += r[2]
            capacity -= r[1]
        else:
            total += r[0] * capacity
            capacity = 0
  
    return total
哈夫曼编码

哈夫曼编码(Huffman Coding)是一种可变长度编码(Variable Length Code),它的主要优势是在固定长度的编码之上,使得出现次数多的字符使用的编码较短。这将减少存储空间、传输成本以及处理时间。

哈夫曼编码的贪心思想是:将出现概率最小的两个字符合并为一个新的字符,其出现概率为这两个字符的出现概率之和。重复以上步骤直到只剩下一个字符。

代码示例:

from queue import PriorityQueue
from typing import Tuple
  
def huffman(symbols: str, frequencies: Tuple[int]) -> Tuple[Dict[str, str], str]:
    queue = PriorityQueue()
    for i, symbol in enumerate(symbols):
        queue.put((frequencies[i], symbol))
    while queue.qsize() > 1:
        left = queue.get()
        right = queue.get()
        for symbol in left[1]:
            self.encoding[symbol] += '0'
        for symbol in right[1]:
            self.encoding[symbol] += '1'
        queue.put((left[0] + right[0], left[1] + right[1]))
    root = queue.get()
    return self.encoding, root[1]
总结

贪婪算法的效率通常很高,但它只考虑当前状态下的局部最优解,不能保证一定得到全局最优解。应用贪婪算法时,需要确保问题满足无后效性、最优子结构性质,并且算法的“贪心策略”需要经过严谨的证明。