Я пытаюсь решить Knight's Open Tour в Haskell и найти решение для генерации всех возможных решений:
knightsTour :: Int -> [[(Int, Int)]]
knightsTour size = go 1 [(1, 1)]
where
maxSteps = size^2
isValid (x, y) = x >= 1 && x <= size && y >= 1 && y <= size
go :: Int -> [(Int, Int)] -> [[(Int, Int)]]
go count acc | count == maxSteps = return $ reverse acc
go count acc = do
next <- nextSteps (head acc)
guard $ isValid next && next `notElem` acc
go (count + 1) (next : acc)
fs = replicateM 2 [(*1), (*(-1))]
nextSteps :: (Int, Int) -> [(Int, Int)]
nextSteps (x, y) = do
(x', y') <- [(1, 2), (2, 1)]
[f, f'] <- fs
return (x + f x', y + f' y')
Однако при тестировании с шахматной доской 8 на 8 вышеуказанная функция никогда не останавливается, потому что пространство для решения безумно велико (19 591 828 170 979 904 различных открытых туров согласно 1). Поэтому я хочу найти только одно решение. Сначала я попробовал:
-- First try
head (knightsTour 8)
с надеждой, что ленивые вычисления Haskell могут спасти положение. Но этого не произошло, решение по-прежнему работает вечно. Затем я попытался:
-- second try
import Data.List (find)
import Data.Maybe (fromMaybe)
knightsTour' :: Int -> [(Int, Int)]
knightsTour' size = go 1 [(1, 1)]
where
maxSteps = size^2
isValid (x, y) = x >= 1 && x <= size && y >= 1 && y <= size
go :: Int -> [(Int, Int)] -> [(Int, Int)]
go count acc | count == maxSteps = reverse acc
go count acc =
let
nextSteps' = [step | step <- nextSteps (head acc), isValid step && step `notElem` acc]
in
fromMaybe [] (find (not . null) $ fmap (\step -> go (count+1) (step:acc)) nextSteps')
fs = replicateM 2 [(*1), (*(-1))]
nextSteps :: (Int, Int) -> [(Int, Int)]
nextSteps (x, y) = do
(x', y') <- [(1, 2), (2, 1)]
[f, f'] <- fs
return (x + f x', y + f' y')
Но приведенное выше решение по-прежнему не может быть реализовано, потому что оно по-прежнему работает вечно. Мои вопросы:
- Почему ленивая оценка не может работать так, как я ожидал, и давать только первое найденное решение? На мой взгляд, в обеих попытках требуется только первое решение.
- Как изменить приведенный выше код, чтобы получить только первое решение?
size
(я считаю, что 5 — это наименьшая квадратная доска, на которой есть решения)? - person user2407038   schedule 22.08.2017