如何解决计算给定 Haskell 变化量的最小硬币数量
我正在尝试为 Change-making problem 编写算法。
我假设硬币系统是规范的,这意味着总是选择小于剩余数量的最大硬币的贪婪算法是一种最优算法。
coinChange
是算法的函数。输入是给定硬币系统的金额和面额。该函数输出每种面额所需的最小硬币数量。
例如:
Input : coinChange 34 [1,5,10,25,50,100]
Output : [4,1,0]
coinChange
的类型声明是:
coinChange :: Integer -> [Integer] -> [Integer]
我找到了这个解决方案,我明白它为什么有效:
coinChange 0 ds = take (length ds) (repeat 0)
coinChange c ds = (coinChange (c `rem` d) (init ds) ) ++ [c `quot` d]
where d = last ds
起初,我尝试过:
coinChange c [] = []
coinChange c ds = reverse ( (c `quot` x): coinChange (c `rem` x) xs )
where (x:xs) = reverse ds
我尝试颠倒硬币面额的顺序,所以我从最大的开始。我认为这比使用 last
和 init
更方便。
当我运行这个时,我得到:
coinChange 64 [1,2,20,100]
[64,0]
我在这里得到 64 似乎很奇怪。
如果我使用递归函数,我认为在整个列表上使用 reverse
会造成混淆,因为列表的某些部分将被多次反转。所以我尝试了这个:
coinChange c [] = []
coinChange c ds =reverse( (c `quot` x):(coinChange' (c `rem` x) xs ))
where (x:xs) = (reverse ds)
coinChange' c [] = []
coinChange' c ds = (c`quot`x):(coinChange' (c `rem` x) xs )
where (x:xs) = reverse ds
如果我尝试:
coinChange 64 [1,100]
我以为我会得到 [0,0]
作为我的最终输出:
(64 `quot` 100):(coinChange' (64 `rem` 100) [1,50])
0:((64`quot`50):coinChange' (64`rem`50) [1,20])
0:(1:coinChange' 14 [1,20])
0:(1:((14`quot` 20):coinChange' (14 `rem` 20) [1,10]))
0:(1:(0:coinChange 14 [1,10]))
0:(1:(0:((14`quot` 10) : coinChange' (14 `rem` 10) [1,5])))
0:(1:(0:( 1 : coinChange' (14 `rem` 10) [1,5])))
0:(1:(0:( 1 : coinChange' 4 [1,5])))
0:(1:(0:( 1 : ((4 `quot` 5):coinChange' (4`rem` 5) [1,2]))))
0:(1:(0:( 1 : (0:coinChange' 4 [1,2]))))
0:(1:(0:( 1 : (0:((4`quot`2):coinChange (4`rem`2) [1])))))
0:(1:(0:( 1 : (0:(2:coinChange 0 [1])))))
0:(1:(0:( 1 : (0:(2:((0`quot`1):coinChange' (0`rem` 1) []))))))
0:(1:(0:( 1 : (0:(2:(0:coinChange' 0 []))))))
0:(1:(0:( 1 : (0:(2:(0:[]))))))
reverse [0,0]
final output: [0,0]
但是,当我在终端中运行此代码时,我得到:
coinChange 64 [1,100]
[0,64,0]
为什么我的方法不起作用?
然后我尝试了:
coinChange 0 ds = take (length ds) (repeat 0)
coinChange c ds = reverse( (c `quot` x): coinChange' (c `rem` x) xs )
where (x:xs) = reverse ds
coinChange' 0 ds = take (length ds) (repeat 0)
coinChange' c ds = (c `quot` x): coinChange' (c `rem` x) xs
where (x:xs) = reverse ds
我从已经可用的解决方案中选择了 coinChange 0 ds = take (length ds) (repeat 0)
。
coinChange 64 [1,0]
我认为主要问题在于 where (x:xs) = reverse ds
。为什么这部分代码没有按照我预期的方式工作?
当我使用 let
而不是 where
时,我仍然遇到同样的错误。
coinChange 0 ds = 取(长度 ds)(重复 0)
coinChange c ds = let (x:xs) = 反向 ds
反过来( (c quot
x): coinChange' (c rem
x) xs )
coinChange' 0 ds = take (length ds)(重复 0)
coinChange' c ds = let (x:xs) = 反向 ds
in (c quot
x): coinChange' (c rem
x) xs
我认为问题可能在于我使用了 (x:xs)
。
所以,我尝试了:
coinChange 0 ds = take (length ds) (repeat 0)
coinChange c ds = let xs = reverse ds
in reverse( (c `quot` (head xs)): coinChange' (c `rem` (head xs)) (tail xs) )
coinChange' 0 ds = take (length ds) (repeat 0)
coinChange' c ds = let xs = reverse ds
in (c `quot` (head xs)): coinChange' (c `rem` (head xs)) (tail xs)
这仍然给出了同样的错误。
我尝试使用串联运算符 (++)
,而不是前置运算符 :
,以查看是否有帮助:
coinChange 0 ds = take (length ds) (repeat 0)
coinChange c ds = let xs = reverse ds
in coinChange (c `rem` (head xs)) (tail xs) ++ [c `quot` (head xs)]
问题似乎出在我颠倒硬币面额列表的方法上。
为什么我所有的尝试都失败了?当商应该是 1 时,为什么我总是收到 64 的这个错误?
解决方法
在这个答案中,我将重点关注你的这个尝试:
coinChange c [] = []
coinChange c ds =reverse( (c `quot` x):(coinChange' (c `rem` x) xs ))
where (x:xs) = (reverse ds)
coinChange' c [] = []
coinChange' c ds = (c`quot`x):(coinChange' (c `rem` x) xs )
where (x:xs) = reverse ds
我认为问题确实出在 where (x:xs) = reverse ds
函数中的 coinChange'
部分。每次递归调用都会反转面额列表。因此,您从该 xs
子句中的模式匹配中获得的 where
是没有最大面额的面额的反向列表。但该列表仍然是反向的,因此您不应再次将其作为参数传递给 coinChange
或 coinChange'
函数。您可以在将其传递给递归调用之前再次反转此列表,但这不是很有效。我建议只颠倒 coinChange
函数中的面额。
另外,coinChange
函数可以简化,因为它基本上只需要颠倒面额和结果列表即可。
效果如下:
coinChange c ds = reverse (coinChange' c (reverse ds))
coinChange' c [] = []
coinChange' c (x:xs) = (c `quot` x) : coinChange' (c `rem` x) xs
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。