Instead of a list-comprehension you can use a recursive call with do
-notation for the list-monad.
It's a bit more tricky as you have to handle the edge-cases correctly and I allowed myself to optimize a bit:
solve :: Integer -> [Integer] -> [[Integer]]
solve 0 ds = [replicate (length ds) 0]
solve _ [] = []
solve n (d:ds) = do
let maxN = floor $ fromIntegral n / fromIntegral (d^2)
x <- [0..maxN]
xs <- solve (n - x * d^2) ds
return (x:xs)
it works like this:
- It's keeping track of the remaining sum in the first argument
- when there this sum is
0
where are obviously done and only have to return 0's (first case) - it will return a list of 0s with the same length as the ds
- if the remaining sum is not
0
but there are no d
's left we are in trouble as there are no solutions (second case) - note that no solutions is just the empty list
- in every other case we have a non-zero
n
(remaining sum) and some ds
left (third case):
- now look for the maximum number that you can pick for
x
(maxN
) remember that x * d^2
should be <= n
so the upper limit is n / d^2
but we are only interested in integers (so it's floor
)
- try all from
x
from 0
to maxN
- look for all solutions of the remaining sum when using this
x
with the remaining ds
and pick one of those xs
- combine
x
with xs
to give a solution to the current subproblem
The list-monad's bind will handle the rest for you ;)
examples
λ> solve 12 [1,2,3]
[[0,3,0],[3,0,1],[4,2,0],[8,1,0],[12,0,0]]
λ> solve 37 [2,3,4,6]
[[3,1,1,0],[7,1,0,0]]
remark
this will fail when dealing with negative numbers - if you need those you gonna have to introduce some more cases - I'm sure you figure them out (it's really more math than Haskell at this point)