我正在尝试实现Kosaraju的图算法,处理一个包含350万行数据的文件,每一行都是两个整数(以空格分隔)表示图的边。首先,我需要创建一个汇总数据结构,其中包含节点及其入边和出边列表。下面的代码实现了这一点,但需要一分钟以上的时间,而我可以从MOOC论坛上的帖子中看到,使用其他语言的人只需要不到10秒钟。 (getLines
需要10秒,而我读到的基准测试中只需不到1秒。)
我对Haskell还很陌生,并使用foldl'
实现了累加方法('
是使其终止的关键),但感觉风格相当命令式,并希望这就是它运行缓慢的原因。此外,我当前计划使用类似的模式进行深度优先搜索,担心所有这些操作都会变得太慢。
我找到了这篇演示文稿和这篇博客,讨论了这些问题,但对于我来说太高级了。
import System.IO
import Control.Monad
import Data.Map.Strict as Map
import Data.List as L
type NodeName = Int
type Edges = [NodeName]
type Explored = Bool
data Node = Node Explored (Edges, Edges) deriving (Show)
type Graph1 = Map NodeName Node
getLines :: FilePath -> IO [[Int]]
getLines = liftM (fmap (fmap read . words) . lines) . readFile
getLines' :: FilePath -> IO [(Int,Int)]
getLines' = liftM (fmap (tuplify2 . fmap read . words) . lines) . readFile
tuplify2 :: [a] -> (a,a)
tuplify2 [x,y] = (x,y)
main = do
list <- getLines "testdata.txt" -- [String]
--list <- getLines "SCC.txt" -- [String]
let
list' = createGraph list
return list'
createGraph :: [[Int]] -> Graph1
createGraph xs = L.foldl' build Map.empty xs
where
build :: Graph1-> [Int] -> Graph1
build = \acc (x:y:_) ->
let tmpAcc = case Map.lookup x acc of
Nothing -> Map.insert x (Node False ([y],[])) acc
Just a -> Map.adjust (\(Node _ (fwd, bck)) -> (Node False ((y:fwd), bck))) x acc
in case Map.lookup y tmpAcc of
Nothing -> Map.insert y (Node False ([],[x])) tmpAcc
Just a -> Map.adjust (\(Node _ (fwd, bck)) -> (Node False (fwd, (x:bck)))) y tmpAcc