如何获得具有在Haskell中提供下一个可能步骤的函数的拼图的解决方案

时间:2015-06-12 06:44:03

标签: haskell

我正在解决Brigde and torch problem

在Haskell。

我写了一个函数给出了一个难题的状态,就像人们还没有交叉,那些已经交叉的人,给出了从一侧到另一侧的所有可能移动的列表(移动两个人前进和一个倒退的人。)

module DarkBridgeDT where


data Crossing = Trip [Float] [Float] Float deriving (Show)
data RoundTrip = BigTrip Crossing Crossing deriving (Show)

trip :: [Float] -> [Float] -> Float -> Crossing
trip x y z                  = Trip x y z    

roundtrip :: Crossing -> Crossing -> RoundTrip
roundtrip x y               = BigTrip x y


next :: Crossing -> [RoundTrip]
next (Trip [] _ _)              = []
next (Trip (a:b:[]) s _ )   
    |a>b                    = [BigTrip (Trip [] (a:b:s) a) (Trip [] [] 0)]
    |otherwise              = [BigTrip (Trip [] (b:a:s) b) (Trip [] [] 0)]
next (Trip d s _)               = [BigTrip (Trip [x,z] (i:j:s) j) b | i <- d, j <- d, i < j, x <- d, z <- d, x < z, z /= i, z /= j, x /= z, x /= i, x /= j, b <- (back [x,z] (i:j:s))]
    where
        back [] s           = []
        back d s            = [Trip (i:d) (filter (/= i) s) i | i <- s]

现在我需要一个函数,它给出一个上面的状态,并且最大时间量会在不到给定时间内给出谜题的所有可能解决方案。

我所拥有的就是:

cross :: Crossing -> Float -> [[RoundTrip]]
cross (Trip [] _ _) _               = []
cross (Trip _ _ acu) max 
    | acu > max                 = [] 
cross (Trip a b acu) max                = map (cross (map (crec) (next (Trip a b acu)) acu)) max
    where
        crec (BigTrip (Trip _ _ t1) (Trip a b t2)) acu  = (Trip a b (t1+t2+acu))

当然不能编译,第5行是让我疯狂的那一行。有什么输入吗?

编辑: cross函数用于将next函数应用于调用的最后一个next函数的每个结果。 如果next的第一个结果类似于:[A,B,C,D]那么它会在ABC和D上调用next以查看是否有任何或所有这些结果都是在max以内找到解决方案(ABC和D将是Crossings里面包含浮动的,这些浮动是广告的时间并与max进行比较。

我的数据结构是

交叉:包含桥梁的第一面(人们用它们穿过桥梁所代表的时间)桥梁的另一边(与另一边相同)和一个代表最大时间的时间最后越过桥梁(在第一个十字路口中最大的两个或在第二个十字路口中唯一的一个)或跨越桥梁的时间量(在十字功能中)。

RoundTrip:代表两个交叉点,第一个和第二个交叉点,一个到达安全点,另一个到达危险点。

cross (Trip [1,2,5,10] [] 0) 16应该给出一个空列表,因为没有解决方案需要不到17分钟(或任何时间单位)。

cross (Trip [1,2,5,10] [] 0) 17应该将拼图的正常解决方案作为往返列表。

我希望这更清楚。

EDIT2: 我终于明白了。在我完成我之前,我阅读了Carsten的解决方案,我们几乎完全相同。他使用了更高级的语法和更复杂的结构,但它非常相似:

module DarkBridgeST where

data Torch = Danger | Safety deriving (Eq,Show)
data State = State
   [Float] -- people in danger
   [Float] -- people safe
   Torch   -- torch position
   Float   -- remaining time
   deriving (Show)

type Crossing = [Float]

classic :: State
classic = State [1,2,5,10] [] Danger 17

next :: State -> [Crossing] -- List all possible moves
next (State []     _    _      _)           = []  -- Finished
next (State _        []   Safety _)         = []  -- No one can come back
next (State danger _    Danger rem)         = [[a,b] | a <- danger, b <- danger, a /= b, a < b, max a b <= rem]
next (State _        safe Safety rem)       = [[a] | a <- safe, a <= rem]


cross :: State -> Crossing -> State  -- Crosses the bridge depending on where the torch is
cross (State danger safe Danger rem) cross  = State (taking cross danger) (safe ++ cross) Safety (rem - (maximum cross))
cross (State danger safe Safety rem) cross  = State (danger ++ cross) (taking cross safe) Danger (rem - (maximum cross))

taking :: [Float] -> [Float] -> [Float]
taking [] d                         = d
taking (x:xs) d                 = taking xs (filter (/=x) d)

solve :: State -> [[Crossing]]
solve (State [] _ _ _)              = [[]]
solve sf = do
    c <- next sf
    let sn = cross sf c
    r <- solve sn
    return (c:r)

总而言之,感谢所有人。我是Haskell编程的新手,这帮助我理解了很多东西。我希望这篇文章也可以帮助有人像我一样开始像我这样的人:)

2 个答案:

答案 0 :(得分:2)

我不会在这里保留大部分代码。

第一个问题是数据结构。 Crossing实际上并不代表与过桥相关的任何事情,而是在过桥之前或之后的状态。并且您不能使用RoundTrip因为桥接交叉的数量总是奇数。

我正在重命名我实际保留的数据结构,但我没有保持不变。

data Bank = Danger | Safety deriving (Eq,Show)
data PuzzleState = PuzzleState
  [Float] -- people still in danger
  [Float] -- people on the safe bank
  Bank -- current location of the torch
  Float -- remaining time
type Crossing = ([Float],Bank)

修改/编写这些功能留给读者练习

next :: PuzzleState -> [Crossing] -- Create a list of possible crossings
applyCrossing :: PuzzleState -> Crossing -> PuzzleState -- Create the next state

然后像这个函数可以把它们放在一起(如果剩余时间太短,假设next返回一个空列表):

cross (PuzzleState [] _ _ _) = [[]]
cross s1 = do
  c <- next s1
  let s2 = applyCrossing s1 c
  r <- cross s2
  return $ c : r

答案 1 :(得分:0)

只是为了好玩,使用懒树的方法:

.helper{
    display:inline-block;
    height:100%;
    vertical-align:middle;
}
/*container*/
.metd{
    vertical-align:middle;
    display:inline-block;
}

然后你可以得到一份解决方案清单:

import Data.List
import Data.Tree

type Pawn = (Char, Int)

data Direction = F | B

data Turn = Turn {
  _start :: [Pawn],
  _end   :: [Pawn],
  _dir   :: Direction,
  _total :: Int
}

type Solution = ([String], Int)

-- generate a tree
mkTree :: [Pawn] -> Tree Turn
mkTree p = Node{ rootLabel = s, subForest = branches s }
  where s = Turn p [] F 0

-- generates a node for a Turn
mkNode :: Turn -> Tree Turn
mkNode t = Node{ rootLabel = t, subForest = branches t }

-- next possible moves
branches :: Turn -> [Tree Turn]
-- complete
branches (Turn [] e d t) = []
-- moving forward
branches (Turn s e F t) = map (mkNode.turn) (next s)
  where
    turn n = Turn (s\\n) (e++n) B (t+time n)
    time = maximum . map snd
    next xs = [x| x <- mapM (const xs) [1..2], head x < head (tail x)]
-- moving backward
branches (Turn s e B t) = map (mkNode.turn) e
  where
    turn n = Turn (n:s) (delete n e) F (t+time n)
    time (_,b) = b

solve :: Int -> Tree Turn -> [Solution]
solve limit tree = solve' [] [] limit tree
  where 
    solve' :: [Solution] -> [String] -> Int -> Tree Turn -> [Solution]
    solve' sols cur limit (Node (Turn s e d t) f)
      | and [t <= limit, s == []] = sols ++ [(cur++[step],t)]
      | t <= limit = concat $ map (solve' sols (cur++[step]) limit) f
      | otherwise = []
      where step = "[" ++ (v s) ++ "|" ++ (v e) ++ "]"
            v    = map fst

或者还生成一个解决方案树:

solve 16 $ mkTree [('a',2), ('b',4), ('c',8)]

=> [(["[abc|]","[c|ab]","[ac|b]","[|bac]"],14),(["[abc|]","[c|ab]","[bc|a]","[|abc]"],16),(["[abc|]","[b|ac]","[ab|c]","[|cab]"],14),(["[abc|]","[a|bc]","[ba|c]","[|cab]"],16)]

然后:

draw :: Int -> Tree Turn -> Tree String
draw limit (Node (Turn s e d t) f)
  | t > limit = Node "Time Out" []
  | s == []   = Node ("Complete: " ++ step) []
  | otherwise = Node step (map (draw limit) f)
  where step = "[" ++ (v s) ++ "|" ++ (v e) ++ "]" ++ " - " ++ (show t)
        v    = map fst

将导致:

putStrLn $ drawTree $ draw 16 $ mkTree [('a',2), ('b',4), ('c',8)]