貪心算法

  貪心算法(又稱貪婪算法)是指,在堆問題求解時,老是作出當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出是在某種意義上的局部最優解python

  貪心算法並不保證會獲得最優解,可是在某些問題上貪心算法的解就是最優解。要會判斷一個問題可否用貪心算法來計算。算法

1、找零問題

  假設商店老闆須要找零n元錢,錢幣的面額有:100元、50元、20元、5元、1元,如何找零使得所需錢幣的數量最小?app

t = [100, 50, 20, 5, 1]

def change(t, n):   # n指的總金額
    m = [0 for _ in range(len(t))]   # 建立一個和t同樣長但全是0的列表
    # 這裏假設t都是倒序排好的
    for i, money in enumerate(t):
        m[i] = n // money  # n整除money:376//100=3
        n = n % money  # n對money取餘:376%100=76
    return m, n   # 若是到最後找不開,n就是找不開的錢

print(change(t, 376))    # ([3, 1, 1, 1, 1], 0)   即:300+50+20+5+1

# 若是t = [100, 50, 20, 5],則有1塊錢找不開,
print(change(t, 451))    # ([4, 1, 0, 0], 1)  

2、揹包問題

  一個小偷在某個商店發現有n個商品,第i個商品價值vi元,重wi千克。他但願拿走的價值儘可能高,但他的揹包最多隻能容納W千克的東西。他應該拿走哪些商品?函數

一、0-1揹包

  對於一個商品,小偷要麼把它完整拿走,要麼留下。不能只拿走一部分,或把一個商品拿走屢次。(商品爲金條)優化

二、分數揹包

  對於一個商品,小偷能夠拿走其中任意一部分。(商品爲金砂)spa

三、問題示例

  • 商品1:v1=60   w1=10
  • 商品2:v2=100   w2=20
  • 商品3:v3=120   w3=30
  • 揹包容量:W=50
  • 對於0-1揹包和分數揹包,貪心算法是否都能獲得最優解?爲何?

  答:貪心算法對分數揹包能夠獲得最優解,對0-1揹包得不到最優解,極可能揹包都沒法裝滿。blog

  兩種揹包問題都具備最優子結構性質。對0-1揹包問題,考慮重量不超過W而價值最高的裝包方案。若是咱們將商品j今後方案中刪除,則剩餘商品必須是重量不超過W-wj的價值最高的方案(小偷只能從不包括商品j的n-1個商品中選擇拿走哪些)。排序

  雖然兩個問題類似,但咱們用貪心策略能夠求解揹包問題,而不能求解0-1揹包問題,爲了求解部分數揹包問題,咱們首先計算每一個商品的每磅價值vi/wi。遵循貪心策略,小偷首先儘可能多地拿走每磅價值最高的商品,若是該商品已所有拿走而揹包未裝滿,他繼續儘可能多地拿走每磅價值第二高的商品,依次類推,直到達到重量上限W。所以,經過將商品按每磅價值排序,貪心算法的時間運行時間是O(nlgn)。字符串

  爲了說明貪心這一貪心策略對0-1揹包問題無效,考慮下圖所示的問題實例。此例包含3個商品和一個能容納50磅重量的揹包。商品1重10磅,價值60美圓。商品2重20磅,價值100美圓。商品3重30磅,價值120美圓。所以,商品1的每磅價值爲6美圓,高於商品2的每磅價值5美圓和商品3的每磅價值4美圓。所以,上述貪心策略會首先拿走商品1。可是,最優解應該是商品2和商品3,而留下商品1。拿走商品1的兩種方案都是次優的。it

  可是,對於分數揹包問題,上述貪心策略首先拿走商品1,是能夠生成最優解的。拿走商品1的策略對0-1揹包問題無效是由於小偷沒法裝滿揹包,空閒空間下降了方案的有效每磅價值。在0-1揹包問題中,當咱們考慮是否將一個商品裝入揹包時,必須比較包含此商品的子問題的解與不包含它的子問題的解,而後才能作出選擇。這會致使大量的重疊子問題——動態規劃的標識。

四、分數揹包代碼實現

def fractional_backpack(goods, w):
    """
    貪心算法——分數揹包
    :param goods: 商品
    :param w: 揹包容量
    :return:
    """
    m = [0 for _ in range(len(goods))]   # [0, 0, 0]
    total_v = 0    # 總價值
    for i, (price, weight) in enumerate(goods):
        if w >= weight:
            m[i] = 1
            total_v += price
            w -= weight
        else:  # 不足1
            m[i] = w / weight
            total_v += m[i]*price
            w = 0
            break
    return m, total_v

print(fractional_backpack(goods, 50))   # ([1, 1, 0.6666666666666666], 240.0)   60+100+120*2/3=60+100+80=240

3、拼接最大數字問題

  有n個非負整數,將其按照字符串拼接的方式拼接爲一個總體。如何拼接可使得獲得的整數最大?

  例:32,94,128,1286,6,71能夠拼接出的最大整數爲94716321286128

from functools import cmp_to_key   # 傳遞python2中sort的cmp函數

li = [32, 94, 128, 1286, 6, 71]

def xy_cmp(x, y):
    if x+y < y+x:  # 要讓大的在前面,這裏須要交換
        return 1
    elif x+y > y+x:
        return -1
    else:
        return 0

def number_join(li):
    li = list(map(str, li))   # 轉換爲字符串:['32', '94', '128', '1286', '6', '71']
    # 方法1:cmp函數
    li.sort(key=cmp_to_key(xy_cmp))  # 傳遞進xy_cmp函數
    # 方法2:使用快排、冒泡排序等
    return "".join(li)

print(number_join(li))    # 94716321286128

4、活動選擇問題

  假設有n個活動,這些互動要佔用同一片場地,而場地在某時刻只能供一個活動使用。

  每一個活動都有一個開始時間si結束時間fi(題目中時間以整數表示),表示活動在[si, fi)(左閉右開)區間佔用場地。

  問:安排哪些活動可以使該場地舉辦的活動個數最多

  

一、貪心結論和證實

  貪心結論:最早結束的活動必定是最優解的一部分

  證實:假設a是全部活動中最早結束的活動,b是最優解中最早結束的活動。

  • 若是a=b,結論成立。
  • 若是a≠b,則b的結束時間必定晚於a的結束時間,則此時用a替換掉最優解中的b,a必定不與最優解中的其餘活動時間重疊,所以替換後的解也是最優解。

 二、活動選擇問題實現

activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)]

# 保證活動是按照結束時間排好序的(每次選擇最先結束的)
activities.sort(key=lambda x: x[1])
# print(activities)

def activity_selection(a):
    """
    活動選擇
    :param a: 活動
    :return:
    """
    res = [a[0]]  # 將最先結束的活動放入結果中
    for i in range(1, len(a)):   # 對列表進行遍歷(除去第一個)
        if a[i][0] >= res[-1][-1]:  # 當前活動開始時間 大於等於 res最後一個活動的結束時間 所以不衝突
            res.append(a[i])
    return res

print(activity_selection(activities))   # [(1, 4), (5, 7), (8, 11), (12, 16)]

5、總結

  都是最優化問題。大大提高了解決問題的速度,可是它也有一些優化問題沒法解決或獲得的不是最優解。

貪心算法存在的問題:

  • 不能保證求得的最後解是最佳的;
  • 不能用來求最大或最小解問題;
  • 只能求知足某些約束條件的可行解的範圍。
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息