Search code examples
haskellbacktracking

Why does my backtracking always return an empty list?


I have a function, test p xs = [(x,y) | (x:ys) <- tails xs, y <- ys, p x y], which returns a list of all tuples which fulfil a predicate such as (\x -> \y -> x*y < 45), for a specified list such as [2..11]. I want to create a maximum maximal independent set: each number in the list is included once, no more and no less. A sample solution for the above predicate and list would be [(2,11),(3,10),(4,9),(5,8),(6,7)].

My current backtracking script is as follows:

notInList pair list
 | list == [] = True
 | (fst pair) `elem` (tupleToList list) || (snd pair) `elem` (tupleToList list) = False
 | otherwise = True

gen pairs final len = do
 pair <- pairs
 guard $ notInList pair final
 if (length final) == len
 then return [pair]
 else do
  next <- gen (delete pair pairs) (pair : final) len
  return $ pair : next

tupleToList :: [(a,a)] -> [a]
tupleToList ((a,b):xs) = a : b : tupleToList xs
tupleToList _ = []

I run gen (test (\x -> \y -> x*y < 45) [2..11]) [] 5, which as far as I can tell should backtrack to the right solution eventually, but the script always returns an empty list. I've tried it on other lists and predicates with a valid solution too, and I'm not sure what's going wrong and how I could fix this script to do what I need it to do. I'm not sure whether returning the empty list suggests that it found no solutions, or if it just breaks somewhere in the middle.


Solution

  • This part is suspicious:

     guard $ notInList pair final
     if (length final) == len
    

    Suppose final is a maximal independent set, so length final == len, where len is the length of the result that you know in advance. Then the guard must be false, so you backtrack without reaching that if.

    Put the test of whether you got the final result at the beginning of the function:

    gen :: Eq a => [(a, a)] -> [(a, a)] -> Int -> [[(a, a)]]
    gen pairs final len | length final == len = return final
    gen pairs final len | otherwise = do
     -- Take a "pair" out and shadow "pairs" with the remainder.
     -- Use "tails" to avoid creating duplicate sets.
     -- For example, if you have a list [p1, p2, ...] you want
     -- to try to pick p1 then p2, but after backtracking, it's no use trying p2 then p1.
     pair : pairs <- tails pairs
     guard $ notInList pair final
     gen pairs (pair : final) len