N-queens in Haskell without list traversal

前端 未结 5 450
悲&欢浪女
悲&欢浪女 2021-02-02 14:07

I searched the web for different solutions to the n-queens problem in Haskell but couldn\'t find any that could check for unsafe positions in O(1) time, like that one that you k

5条回答
  •  别跟我提以往
    2021-02-02 14:45

    The basic potential problem with this approach is that the arrays for the diagonals need to be modified every time a queen is placed. The small improvement of constant lookup time for the diagonals might not necessarily be worth the additional work of constantly creating new modified arrays.

    But the best way to know the real answer is to try it, so I played around a bit and came up with the following:

    import Data.Array.IArray (array, (//), (!))
    import Data.Array.Unboxed (UArray)
    import Data.Set (Set, fromList, toList, delete)
    
    -- contains sets of unoccupied columns and lookup arrays for both diagonals
    data BoardState = BoardState (Set Int) (UArray Int Bool) (UArray Int Bool)
    
    -- an empty board
    board :: Int -> BoardState
    board n
       = BoardState (fromList [0..n-1]) (truearr 0 (2*(n-1))) (truearr (1-n) (n-1))
       where truearr a b = array (a,b) [(i,True) | i <- [a..b]]
    
    -- modify board state if queen gets placed
    occupy :: BoardState -> (Int, Int) -> BoardState
    occupy (BoardState c s d) (a,b)
       = BoardState (delete b c) (tofalse s (a+b)) (tofalse d (a-b))
       where tofalse arr i = arr // [(i, False)]
    
    -- get free fields in a row
    freefields :: BoardState -> Int -> [(Int, Int)]
    freefields (BoardState c s d) a = filter freediag candidates
       where candidates = [(a,b) | b <- toList c]
             freediag (a,b) = (s ! (a+b)) && (d ! (a-b))
    
    -- try all positions for a queen in row n-1
    place :: BoardState -> Int -> [[(Int, Int)]]
    place _ 0 = [[]]
    place b n = concatMap place_ (freefields b (n-1))
       where place_ p = map (p:) (place (occupy b p) (n-1))
    
    -- all possibilities to place n queens on a n*n board
    queens :: Int -> [[(Int, Int)]]
    queens n = place (board n) n
    

    This works and is for n=14 roughly 25% faster than the version you mentioned. The main speedup comes from using the unboxed arrays bdonian recommended. With the normal Data.Array it has about the same runtime as the version in the question.

    It might also be worth it to try the other array types from the standard library to see if using them can further improve performance.

提交回复
热议问题