硬币找零之贪婪算法|Go主题月

1,258 阅读2分钟

优化问题经典例子:找零时使用最少数量的硬币。

假设某漂亮国小区24小时自动售货机每笔现金交易(暂不支持移动支付)找零时使用最少数量的硬币以减少硬币补充次数节省成本。

例如,张三使用一元大钞(1元=100分)购买了总共价值37分的榨菜、茶叶蛋和肥仔快乐水,假设硬币面值有1分、5分、10分和25分,那么应该找回多少个硬币呢?

答案是6枚:25 分的 2 枚,10 分的 1 枚,1 分的 3 枚。

如何计算呢?

从面值最大的硬币(25 美分)开始,使用尽可能多的硬币,然后尽可能多地使用面 值第 2 大的硬币。这种方法叫作贪婪算法——试图最大程度地解决问题。

用Golang实现的贪婪算法下的找零时使用最少数量的硬币的代码如下:

package main

import (
	"fmt"
        "sort"
)

func minCoinsGreed(coinValus []int, change int) int {
	// 找零时使用最少数量硬币:贪婪算法
	// 从面值最大的硬币(25 美分)开始,使用尽可能多的硬币,
	// 然后尽可能多地使用面值第 2 大的硬币。
	// 确保硬币面值降序排序
	sort.Sort(sort.Reverse(sort.IntSlice(coinValus)))
	coins := 0
	for _, va := range coinValus {
		// 商表示可以使用此面值的硬币数量
		div := change / va
		// 累计硬币数量
		coins += div
		// 余数为还需要找零的面值
		change = change % va
		// 如果已找零完毕则提前退出循环
		if change == 0 {
			break
		}
	}

	return coins

}

func main() {
	coinValues := []int{25, 10, 5, 1}
	fmt.Println("需要的硬币数量:", minCoinsGreed(coinValues, 63))
	// 需要的硬币数量: 6
	coinValus = []int{25, 21, 10, 5, 1}
	fmt.Println("需要的硬币数量:", minCoinsGreed(coinValues, 63))
	// 需要的硬币数量: 6
}

如果找零面值里面支持21分,按照此贪婪算法计算出来的结果却是6枚。
实际最少硬币数量应该是3枚21分的硬币即可。
所以此贪婪算法在计算找零最少硬币数量未必得到最优解。

要一定得到最优解,下一篇将尝试优雅的递归解决硬币找零