【发布时间】:2015-01-07 10:39:58
【问题描述】:
我想找到硬币找零的所有组合。 1, 2, 5, 10, 20, 50, 100 和 200. (1 cent , 2cent ..) 如果硬币超过 500 (5 欧元),它应该给出 -1。我的代码与这些测试用例完美配合: numOfSplits 10 (11) numOfSplits 20 (41) numOfSplits 100 (4563) 。当我尝试使用测试用例 numOfSplits 200 或 500 时,它会给出 C 堆栈溢出错误。我怎样才能让我的代码变得更好?
numOfSplits :: Integer -> Integer
numOfSplits a
| (abs a) > 500 = -1
| (abs a) == 0 = 0
| otherwise = intzahler (makeChange [200,100,50,20,10,5,2,1] (abs a) 200)
intzahler :: [[Integer]] -> Integer
intzahler array
| array == [] = 0
| otherwise = 1 + intzahler (tail array)
makeChange :: [Integer] -> Integer -> Integer -> [[Integer]]
makeChange coins amount maxCoins
| amount < 0 = []
| amount == 0 = [[]]
| null coins = []
| amount `div` maximum coins > maxCoins = [] -- optimisation
| amount > 0 =
do x <- coins
xs <- makeChange (filter (<= x) coins)
(amount - x)
(maxCoins - 1)
guard (genericLength (x:xs) <= maxCoins)
return (x:xs)
我将我的代码更改为此代码,我不再遇到堆栈溢出错误,但现在我的代码运行缓慢。示例:对于 numOfSplits 500 ,它需要超过 30 分钟,我怎样才能更快地做到这一点?
numOfSplits :: Integer -> Integer
numOfSplits a
| (abs a) > 500 = -1
| (abs a) == 0 = 0
| otherwise = fromIntegral . length $ makeChange [200,100,50,20,10,5,2,1] (abs a)
makeChange :: [Integer] -> Integer -> [[Integer]]
makeChange coins amount
| amount < 0 = []
| amount == 0 = [[]]
| null coins = []
| amount > 0 =
do x <- coins
xs <- makeChange (filter (<= x) coins) (amount - x)
return (x:xs)
【问题讨论】:
-
旁注:在 Haskell 世界中,使用 -1 表示错误是非常严重的。使用
Maybe Integer执行此操作(Just x表示成功,Nothing表示失败)。 -
使用 Prelude.genericLength 而不是 intzahler 可以帮助编译器找出更好的执行路径...
-
您不希望任何长度超过
Int的大小(我希望它很长),所以我建议使用fromIntegral . length而不是genericLength,两者都在您使用genericLength代替intzahler。genericLength真的适用于当你有一个奇怪的Num实例时,比如懒惰的自然。它不是尾递归的,因此对于像Int和Integer这样的严格数据类型来说非常糟糕,并且肯定会导致堆栈溢出。 -
@luqui,
genericLength与Int和Integer实际上有一对令人作呕的规则,但我同意你的观点,应该避免它。 -
模式匹配是个好东西。另外,你为什么要取绝对值?此外,
div相当昂贵(尤其是使用 GHC 当前的本机代码生成器),因此您应该避免使用它。两边都乘以maximum coins,把事情变成乘法。
标签: haskell recursion stack-overflow coin-change