硬幣找零問題算法幾種不同的代碼實現方式(使用Python實現)


問題描述

  假設你為一家自動售貨機廠家編程序,自動售貨機要每次找給顧客最少數量硬幣;假設某次顧客投進$1紙幣,買了ȼ37的東西,要找ȼ63,那么最少數量就是:2個quarter(ȼ25)、1個dime(ȼ10)和3個penny(ȼ1),一共6個.
  分別使用貪心算法,遞歸,以及遞歸的優化版本:遞歸 + 備忘錄技術,與動態規划四種解法

   問題抽象: coin_list = [1,10,25,100]. coin = 63. 在所給定coin_list等於coin最少個數.(63的最優組合是25 25 10 1 1 1.一共6個)




解法一:貪心算法

def get_min_coins(money_value):  # 貪心版本
    '''
    貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。
    也就是說,不從整體最優上加以考慮,他所做出的是在某種意義上的局部最優解。
    '''
    return_list = []
    money_coins = [1,5,10,25,100] # 不兼容愛爾波尼亞的21分硬幣.其實63的最優解應該是三個21的硬幣.貪心算法的局限性
    money_coins_sorted = sorted(money_coins,reverse=True)

    

    for current_value in money_coins_sorted:

        len_current_value = money_value // current_value 
        return_list += [current_value] * len_current_value
        money_value = money_value - current_value*len_current_value
        '''
        第一次: len_current_value == 2 , return_list = [25,25] , money_value ==  13
        第二次: ...
        第三次: ...
        第四次: ...

        '''
        if money_value <=0: # 截至條件
            break
    return return_list

#print(get_min_coins(63))


解法二:遞歸求解



def recMC(coin_value_list,money_value): # 使用遞歸求得最優解

    '''
    遞歸三條件:
        不斷縮小自身規模
        不斷調用自身
        有結束條件
    其中,遞歸是自頂向下進行計算的.
    '''
    min_len = money_value
    if money_value in coin_value_list: # 遞歸結束條件
       return 1
    else:
        for current_value in [c for c in coin_value_list if c < money_value]:
            now_len = 1 + recMC(coin_value_list,money_value-current_value) # 調用自身,且縮小規模
            if now_len < min_len:
                min_len = now_len

    return min_len

# print(recMC([1,5,10,25], 63)) # 這個程序執行的太慢了.一共需要執行67,716,925次.

解法三:遞歸優化:備忘錄技術



def recDc(coin_value_list,money_value,know_results): # 遞歸改進版本
    '''
    遞歸改進的關鍵是消除重復計算.其中有一種技術是備忘錄技術.

    可以用一個表將計算過的中間結果保存起來,在計算之前查表看看是否已經計算過

    在遞歸調用之前,先查找表中是否已有部分找零的最優解如果有,
    直接返回最優解而不進行遞歸調用如果沒有,才進行遞歸調用
    '''
    min_len = money_value
    print(know_results)
    if money_value in coin_value_list:
        return 1
    elif know_results[money_value] !=  0:
        return know_results[money_value]

    else:
        for i in [c for c in coin_value_list if c < money_value]:
            now_len = 1 + recDc(coin_value_list,money_value-i,know_results)

            if now_len < min_len:
                min_len = now_len
                know_results[money_value] = now_len
                print(know_results)
    
    return min_len

# print(recDc([1,5,10,25],63,[0]*64))

解法四:動態規划



def dpMakeChange (coinValueList,change,minCoins): # 動態規划解法
    '''
    動態規划:其所求的最優解是由子問題的最優解構成的.
    
    動態規划是自底向上的計算.

    在找零遞加的過程中,設法保持每一分錢的遞加都是最優解,一直加到求解找零錢
    數,自然得到最優解
    '''
    for coin in range(1,change+1):
        
        min_len = coin

        for i in [ c for c in coinValueList if c <=coin]:
            if minCoins[coin-i] + 1 < min_len:
                min_len = minCoins[coin-i]+1
        
        minCoins[coin] = min_len
    return minCoins[change]
    

# print(dpMakeChange([1,5,10,25],63,[0]*64))

解法四擴展:動態規划擴展


def dp_make_change(coin_value_list,money_value,minCOins,coins_record):# coins_record記錄每一個最優解所添加的硬幣.用於返回最優硬幣
    '''

    '''
    for coin in range(1,money_value+1):
        
        min_len = coin
        new_add_coin =1

        for i in [c for c in coin_value_list if c <= coin]:
            
            if minCOins[coin-i] + 1 < min_len:
                min_len = minCOins[coin-i] + 1
                new_add_coin = i
        minCOins[coin] = min_len
        coins_record[coin] = new_add_coin
    
    # print(coins_record)
    return minCOins[money_value]

def print_coins(cions_record,how_much_money):
    '''
    在得到最后的解后,減去選擇的硬幣幣值,回溯到表格之前的部分找零,
    就能逐步得到每一步所選擇的硬幣幣值
    '''
    rev_cions = []
    while how_much_money>0:
        how_much_money = how_much_money - cions_record[how_much_money]
        rev_cions.append(cions_record[how_much_money])
    return rev_cions

# some_money = 63
# cions_record = [0]*64
# print(dp_make_change([1,5,10,25],some_money,[0]*64,cions_record))

# print(print_coins(cions_record,some_money))




參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM