必ず解ける迷路
Posted feedbacks - Haskell
Haskellでやってみました。学習中です。とても楽しい問題ですね! 地道にスタート地点からほり続けるアルゴリズムです。 最初はData.Array.Arrayを使いました。ヒープ爆発で没。その後、UArrayを使い、多少改善、 最終的にはIOUArrayに落ち着きました。 その他のメモリ消費・速度対策としては 1.何度も計算する必要のないものを定数化する。 2.深さ優先の再帰から広さ優先の再帰にする。かつ、TailRecursionにした。 3.元の実装は迷路中の位置をすべてタプル(x、y)で表現していたので、配列アクセスのときにインデックスへの 変換が多発していた。ので、インデックスベースの計算の高度も少し書きました。 4.迷路のサイズのグローバル変数化 GHCのプロファイラをはじめて使いました。 結果として、 -- Vista Ultimate on Toshiba Portege M200 (Centrino Duo) --try 1 Elapsed Time : 00:00:05.760 --try 2 Elapsed Time : 00:00:05.787 --try 3 Elapsed Time : 00:00:05.790 といった結果が出ています。 出題の方のコメントのうち、「。限られたメモリを使って縦方向に無限に広い迷路を…」の部分のヒントは 利用していません…使えば、まだまだ早くなると思うのですが、アルゴリズム的なショートカットではないかたちで、 ほかにどんなテクニックで高速化ができるかに興味があります。 考えたけどやってないもののひとつとしては、迷路の1マスを1Bitでやってみるとか… ほかの方のHaskellの投稿もぜひ見せてもらいたいです。何かフィードバックありましたらよろしくお願いします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | module Main
where
import Data.Array.IO
import Control.Monad.State
import System.Random
import Data.List -- for intercalate
type Point = (Int, Int)
type TwoDArray = IOUArray Int Char
type Maze = TwoDArray
ptToIdx :: Point -> Int
ptToIdx (x, y) = (y * rszx) + x
rasterize :: Int -> Int
rasterize i = i * 2 + 1
rasterizePt :: Point -> Point
rasterizePt (x, y) = (rasterize x, rasterize y)
lstMappedPoint :: Point -> [Point] -> [Point]
lstMappedPoint pt pts = map (addPt pt) pts
where
addPt (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
lstMappedIdx :: Int -> [Int] -> [Int]
lstMappedIdx idx idxs = map (idx +) idxs
inBoundForMove :: Point -> Bool
inBoundForMove (x, y)
| x <= 0 || x + 1 >= rszx || y <= 0 || y + 1 >= rszy = False
| otherwise = True
surroundingsIdx :: Point -> [Int]
surroundingsIdx (x, y)
| x == 0 && y == 0 = nbIdxsTopLeft
| x == rszx - 1 && y == 0 = nbIdxsTopRightMapped
| x == 0 && y == rszy - 1 = nbIdxsBottomLeftMapped
| x == rszx - 1 && y == rszy - 1 = nbIdxsBottomRightMapped
| x == 0 = lstMappedIdx (ptToIdx (x, y)) nbIdxsLeft
| x == rszx - 1 = lstMappedIdx (ptToIdx (x, y)) nbIdxsRight
| y == 0 = lstMappedIdx (ptToIdx (x, y)) nbIdxsTop
| y == rszy - 1 = lstMappedIdx (ptToIdx (x, y)) nbIdxsBottom
| otherwise = lstMappedIdx (ptToIdx (x, y)) nbIdxs
onTheSameLineIdx :: [Int] -> Bool
onTheSameLineIdx [] = True
onTheSameLineIdx [i] = True
onTheSameLineIdx (idx:idxs) = (all (sameX idx) idxs) || (all (sameY idx) idxs)
where
sameX idx1 idx2 = (abs $ idx1 - idx2) <= 2
sameY idx1 idx2 = (abs $ idx1 - idx2) == rszy || (abs $ idx1 - idx2) == rszy * 2
left =[0..1] :: [Int]
right =[-1..0] :: [Int]
top =[0..1] :: [Int]
bottom=[-1..0] :: [Int]
rest =[-1..1] :: [Int]
nbptsTopLeft = [(x, y) | x <- left, y <- top, x /= 0 || y /= 0] :: [Point]
nbptsTop = [(x, y) | x <- rest, y <- top, x /= 0 || y /= 0] :: [Point]
nbptsTopRight = [(x, y) | x <- right, y <- top, x /= 0 || y /= 0] :: [Point]
nbptsLeft = [(x, y) | x <- left, y <- rest, x /= 0 || y /= 0] :: [Point]
nbpoints = [(x, y) | x <- rest, y <- rest, x /= 0 || y /= 0] :: [Point]
nbptsRight = [(x, y) | x <- right, y <- rest, x /= 0 || y /= 0] :: [Point]
nbptsBottomLeft = [(x, y) | x <- left, y <- bottom, x /= 0 || y /= 0] :: [Point]
nbptsBottom = [(x, y) | x <- rest, y <- bottom, x /= 0 || y /= 0] :: [Point]
nbptsBottomRight = [(x, y) | x <- right, y <- bottom, x /= 0 || y /= 0] :: [Point]
nbIdxsTopLeft = map ptToIdx nbptsTopLeft
nbIdxsTop = map ptToIdx nbptsTop
nbIdxsTopRight = map ptToIdx nbptsTopRight
nbIdxsLeft = map ptToIdx nbptsLeft
nbIdxs = map ptToIdx nbpoints
nbIdxsRight = map ptToIdx nbptsRight
nbIdxsBottomLeft = map ptToIdx nbptsBottomLeft
nbIdxsBottom = map ptToIdx nbptsBottom
nbIdxsBottomRight = map ptToIdx nbptsBottomRight
nbIdxsTopRightMapped = lstMappedIdx (ptToIdx(rszx - 1, 0)) nbIdxsTopRight
nbIdxsBottomLeftMapped = lstMappedIdx (ptToIdx(0, rszy- 1)) nbIdxsTopRight
nbIdxsBottomRightMapped = lstMappedIdx (ptToIdx(rszx - 1, rszy- 1)) nbIdxsTopRight
movable :: Point -> Bool
movable (x, y) = (abs x) + (abs y) == 1
nbmovables :: [Point]
nbmovables = filter (movable) nbpoints
nbmovablesIdx :: [Int]
nbmovablesIdx = map ptToIdx nbmovables
potentialMoves :: Point -> [Point]
potentialMoves pt = lstMappedPoint pt nbmovables
initialMaze :: IO TwoDArray
initialMaze = newArray (0, rcmz) 'x'
dig :: TwoDArray -> Point -> IO()
dig rg pt = do
writeArray rg (ptToIdx pt) ' '
fetchIdx :: TwoDArray -> Int -> IO Char
fetchIdx rg idx = readArray rg idx
fetch :: TwoDArray -> Point -> IO Char
fetch rg pt = readArray rg (ptToIdx pt)
idxHas :: Maze -> Int -> IO Bool
idxHas mz idx = do
chT <- fetchIdx mz idx
return $ chT == ' '
lookaround :: Maze -> Point -> IO Bool
lookaround mz pt = do
filteredPoints <- filterM (idxHas mz) $ surroundingsIdx pt
return $ onTheSameLineIdx filteredPoints
legalMove :: Maze -> Point -> IO Bool
legalMove mz pt = do
ch <- fetch mz pt
if ch == 'x' then
lookaround mz pt
else
return False
moves :: Point -> [Point]
moves pt = filter (inBoundForMove) $ potentialMoves pt
diggableNbr :: Point -> Point -> Bool
diggableNbr (x1, y1) (x2, y2) = ((abs $ x1 - x2) + (abs $ y1 - y2)) == 1
genMaze :: Maze -> [Point] -> IO()
genMaze mz pts = doRecurse pts
where
doRecurse :: [Point] -> IO()
doRecurse [] = return ()
doRecurse pts = do
ptsDug <- mapDig pts
doRecurse $ concatMap (moves) ptsDug
mapDig :: [Point] -> IO [Point]
mapDig [] = return []
mapDig (x:xs) = do
fDug <- digIfValid x
if (fDug) then do
xsRet <- mapDig xs -- $ filter (diggableNbr x) xs
return (x:xsRet)
else
mapDig xs
digIfValid :: Point -> IO Bool
digIfValid pt = do
fPtValid <- legalMove mz pt
if (fPtValid) then do
dig mz pt
return True
else
return False
dumpRow :: TwoDArray -> Int -> IO()
dumpRow rg iy = do
str <- mapM (readArray rg) $ [(iy * rszx)..(iy * rszx + rszx - 1)]
putStrLn str
dumpMaze :: TwoDArray -> IO ()
dumpMaze rg = do
mapM (dumpRow rg) [0..(rszy - 1)]
return ()
szx = 1024 :: Int
szy = 1024 :: Int
rszx = rasterize szx
rszy = rasterize szy
cmz = szx * szy - 1
rcmz = rszx * rszy - 1
main :: IO()
main = do
mz <- initialMaze
x <- getStdRandom $ randomR (0, szx - 1)
y <- getStdRandom $ randomR (0, szy - 1)
putStrLn $ show (x, y)
genMaze mz [rasterizePt (x, y)]
dumpMaze mz
|

squld
#5275()
Rating9/11=0.82
[ reply ]