【发布时间】:2021-06-17 09:07:39
【问题描述】:
我正在尝试为Change-making problem 编写算法。
我假设硬币系统是规范的,这意味着总是选择小于剩余数量的最大硬币的贪心算法是最优算法。
coinChange 是算法的函数。输入是给定硬币系统的金额和面额。该函数输出每种面额所需的最小硬币数量。
例如:
Input : coinChange 34 [1, 5, 10, 25, 50, 100]
Output : [4,1,0,1,0,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,5,10,20,50,100]
[64,0,0,0,0,0,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,2,5,10,20,50,100]
我以为我会得到[0,2,0,1,0,1,0] 作为我的最终输出:
(64 `quot` 100):(coinChange' (64 `rem` 100) [1,2,5,10,20,50])
0:((64`quot`50):coinChange' (64`rem`50) [1,2,5,10,20])
0:(1:coinChange' 14 [1,2,5,10,20])
0:(1:((14`quot` 20):coinChange' (14 `rem` 20) [1,2,5,10]))
0:(1:(0:coinChange 14 [1,2,5,10]))
0:(1:(0:((14`quot` 10) : coinChange' (14 `rem` 10) [1,2,5])))
0:(1:(0:( 1 : coinChange' (14 `rem` 10) [1,2,5])))
0:(1:(0:( 1 : coinChange' 4 [1,2,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,1,0,1,0,2,0]
final output: [0,2,0,1,0,1,0]
但是,当我在终端中运行此代码时,我得到:
coinChange 64 [1,2,5,10,20,50,100]
[0,0,0,0,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,2,5,10,20,50,100]
[0,0,0,0,0,64,0]
我认为主要问题在于where (x:xs) = reverse ds。为什么这部分代码没有按我预期的方式工作?
当我使用 let 而不是 where 时,我仍然遇到同样的错误。
coinChange 0 ds = take (length ds) (repeat 0)
coinChange c ds = let (x:xs) = reverse ds
反向((cquotx):coinChange'(cremx)xs)
coinChange' 0 ds = take (length ds) (repeat 0)
coinChange' c ds = let (x:xs) = 反向 ds
在 (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 的此错误?
【问题讨论】:
-
感谢您的努力,但您能否将其作为针对一个特定问题的一个集中问题?不需要整个历史,只需代码、输入、所需输出、实际输出/错误、问题。 :)
标签: haskell recursion modular-arithmetic coin-change