我在Haskell中编写了一个小代码。您能否提出改善性能的解决方案?
Height
和Width
的网格。点的位置显示为'#'
4 4 ##.. #... ...# ....
0 0 1 2 0 1 2 1 1 2 1 0 2 3 2 1
要在网格中设置大量数字,我使用了二维IOArray
来减少修改成本。处理500x500的网格需要花费一分钟多的时间,而我在Python中编写的替代代码仅需5秒。
不到16秒将不胜感激!
import Data.List
import Data.Array.IO
import Control.Monad
type Point = (Int, Int)
type Grid = IOArray Point Int
main :: IO ()
main = do
[height, width] <- (map read . take 2 . words) <$> getLine
points <- zeroPoints width <$> getContents
grid <- newArray ((1,1), (height, width)) (-1)
wfs grid points 0
printArray grid
zeroPoints :: Int -> String -> [Point]
zeroPoints width str = [(i `div` width + 1, i `mod` width + 1) | (i, c) <- chars, c == '#']
where chars = zip [0..] (concat $ words str)
wfs :: Grid -> [Point] -> Int -> IO [Point]
wfs grid [] _ = return []
wfs grid points distance = do
mapM_ (\p -> writeArray grid p distance) points
(_, (height, width)) <- getBounds grid
newPoints <- neighbors grid height width points
wfs grid newPoints (distance + 1)
neighbors :: Grid -> Int -> Int -> [Point] -> IO [Point]
neighbors grid height width points =
filterM (isEmpty grid) $ nub $ filter inArea $ [up, down, left, right] <*> points
where
up (x, y) = (x - 1, y)
down (x, y) = (x + 1, y)
left (x, y) = (x, y - 1)
right (x, y) = (x, y + 1)
inArea (x, y) = x > 0 && x <= height && y > 0 && y <= width
isEmpty grid p = (< 0) <$> readArray grid p
printArray :: Grid -> IO ()
printArray grid = do
(_, (height, width)) <- getBounds grid
forM_ [(h, w) | h <- [1..height], w <- [1..width]] $ \(h, w) -> do
i <- readArray grid (h, w)
if w == width then putStrLn $ show i else putStr $ show i ++ " "
答案 0 :(得分:4)
我没有进行任何分析,但这是我跳出来的那一行:
filterM (isEmpty grid) $ nub $ filter inArea $ [up, down, left, right] <*> points
特别地,nub
是O(n ^ 2)运算。您可能首先尝试使用ordNub
中的Data.Containers.ListUtils
。
我看到的另一件事是您正在使用装箱的数组。盒装阵列有间接成本,也有额外的GC成本。他们可以很容易地使使用它们的代码比应有的速度慢几倍。通常,仅在需要盒装数组的多态或懒惰时才应使用盒装数组。我在这里没有立即看到任何东西。
最后,进行微优化:如果您需要商和余数,请使用divMod
或quotRem
仅用一个硬件部门来完成这项工作。这不是您的代码运行缓慢的原因,但这是一个好习惯。如果数字为正,请使用quotRem
来提高速度。如果不是,请确保阅读文档以弄清楚应使用哪个。