0

I want to solve the following problem in Haskell:

Let n be a natural number and let A = [d_1 , ..., d_r] be a set of positive numbers.

I want to find all the positive solutions of the following equation:

n = Sum d_i^2 x_i.

For example if n= 12 and the set A= [1,2,3]. I would like to solve the following equation over the natural numbers:

x+4y+9z=12.

It's enough to use the following code:

[(x,y,z) | x<-[0..12], y<-[0..12], z<-[0..12], x+4*y+9*z==12]

My problem is if n is not fixed and also the set A are not fixed. I don't know how to "produce" a certain amount of variables indexed by the set A.

thor
  • 21,418
  • 31
  • 87
  • 173
Miguel
  • 103
  • 3
  • Are you looking for this `[(x,y,z,n) |n<-[0..12], x<-[0..3], y<-[0..3], z<-[0..3], x+4*y+9*z==n ]` or `[(x,y,z,n) |n<-[0..12], x<-[0..n], y<-[0..n], z<-[0..n], x+4*y+9*z==n ]` ? – nobody May 04 '16 at 01:24
  • I need more variables. The amount of variables is the same as the cardinality of the set A. If the set A is let say A =[ 1, 2, 3 ,4 ,6]. Then the equation is a+ 2b + 9c+ 16d+ 36e = n. – Miguel May 04 '16 at 02:42
  • How much further you need the cardinality go? – nobody May 04 '16 at 04:36

2 Answers2

3

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)

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • The cardinality of set A doesn't change at all, and even 'n' is fixed. – nobody May 04 '16 at 04:46
  • @nobody you should read the question: "My problem is if n is not fixed and also the set A are not fixed." – Random Dev May 04 '16 at 05:19
  • I mean your solution's set A and 'n' do not change. – nobody May 04 '16 at 05:21
  • sorry I have no clue what you are talking about - the two examples use different `n` and different `A`s – Random Dev May 04 '16 at 05:23
  • The author needs the set A to be a set of base variable that could be change by square, if I didn't get his idea wrong. – nobody May 04 '16 at 05:29
  • again - for `n=12` and `A=[1,2,3]` which is one of OPs example above produces the same result as the given comprehension `[(x,y,z) | x<-[0..12], y<-[0..12], z<-[0..12], x+4*y+9*z==12]` - and I'm sorry but I really don't understand what "change by square" is supposed to be here – Random Dev May 04 '16 at 05:33
  • his idea was pretty clear - solve "n = Sum d_i^2 x_i" for given `d_i`s and `n` for `x_i` and above will do exactly this - the resulting lists will be the `x_i` - you can test it : `2^2 * 3 + 3^2 * 1 + 4^2 * 1 + 6^2 * 0 = 37` which is a solution for `n=37` and `A = [2,3,4,6]` – Random Dev May 04 '16 at 05:35
  • aside from him mistaking 2^2=2 instead of 4 that is exactly what I am doing here - the *cardinality* of the resulting lists will be the same as of `A` (where cardinality is just list-length) – Random Dev May 04 '16 at 05:37
  • the only difference here is that here the produced solutions are represented by lists instead of tuples - which should be expected as the length depend on the length of the input and you would need heavy hitters (dependent types) to get tuples with the right length out of this – Random Dev May 04 '16 at 05:41
1

Some hints:

Ultimately you want to write a function with this signature:

solutions :: Int -> [Int] -> [ [Int] ]

Examples:

solutions 4 [1,2]  == [ [4,0], [0,1] ]
  -- two solutions: 4 = 4*1^2 + 0*2^2,  4 = 0*1^2 + 1*2^2

solutions 22 [2,3]  == [ [1,2] ]
  -- just one solution: 22 = 1*2^2 + 2*3^2

solutions 10 [2,3]  == [ ]
  -- no solutions

Step 2. Define solutions recursively based on the structure of the list:

solutions x [a] = ...
  -- This will either be [] or a single element list

solutions x (a:as) = ...
  -- Hint: you will use `solutions ... as` here
ErikR
  • 51,541
  • 9
  • 73
  • 124