这是我的后续行动上一个问题 https://stackoverflow.com/questions/24278006/need-advice-on-optimising-haskell-data-processing关于处理 5.1m 边有向图的向量表示。我正在尝试实现 Kosaraju 的图算法,因此需要按照反向边缘上深度优先搜索(DFS)的完成时间的顺序重新排列我的向量。我的代码可以在小数据集上运行,但在完整数据集上无法在 10 分钟内返回。 (我不能排除大图会产生循环,但我的测试数据上没有任何迹象。)
DFS 需要避免重新访问节点,因此我需要某种“状态”来进行搜索(当前是一个元组,我应该使用 State Monad 吗?)。第一次搜索应该返回一个重新排序的向量,但我目前通过返回重新排序的节点索引列表来保持简单,以便我可以随后一次性处理该向量。
我认为问题在于dfsInner
。下面的代码“记住”访问过的节点,更新每个节点的探索字段(第三个守卫)。尽管我尝试使其尾递归,但代码似乎使内存使用量增长得相当快。我需要执行一些严格的规定吗如果是这样,怎么办? (我有另一个用于单个搜索的版本,它通过查看堆栈上未探索的边缘的起始节点和已完成的节点列表来检查以前的访问。这不会增长得那么快,但是不会返回任何连接良好的节点。)
然而,它也可能是foldr'
, but 我怎样才能检测到?
这应该是 Coursera 作业,但我不再确定我可以勾选荣誉代码按钮!不过,学习更重要,所以我真的不想要复制/粘贴答案。我所拥有的并不是很优雅 - 它也有一种迫切的感觉,这是由保持某种状态的问题驱动的 - 请参阅第三个后卫。我欢迎对设计模式提出意见。
type NodeName = Int
type Edges = [NodeName]
type Explored = Bool
type Stack = [(Int, Int)]
data Node = Node NodeName Explored Edges Edges deriving (Eq, Show)
type Graph = Vector Node
main = do
edges <- V.fromList `fmap` getEdges "SCC.txt"
let
maxIndex = fst $ V.last edges
gr = createGraph maxIndex edges
res = dfsOuter gr
--return gr
putStrLn $ show res
dfsOuter gr =
let tmp = V.foldr' callInner (gr,[]) gr
in snd tmp
callInner :: Node -> (Graph, Stack) -> (Graph, Stack)
callInner (Node idx _ fwd bwd) (gr,acc) =
let (Node _ explored _ _) = gr V.! idx
in case explored of
True -> (gr, acc)
False ->
let
initialStack = map (\l -> (idx, l)) bwd
gr' = gr V.// [(idx, Node idx True fwd bwd)]
(gr'', newScc) = dfsInner idx initialStack (length acc) (gr', [])
in (gr'', newScc++acc)
dfsInner :: NodeName -> Stack -> Int -> (Graph, [(Int, Int)]) -> (Graph, [(Int, Int)])
dfsInner start [] finishCounter (gr, acc) = (gr, (start, finishCounter):acc)
dfsInner start stack finishCounter (gr, acc)
| nextStart /= start = -- no more places to go from this node
dfsInner nextStart stack (finishCounter + 1) $ (gr, (start, finishCounter):acc)
| nextExplored =
-- nextExplored || any (\(y,_) -> y == stack0Head) stack || any (\(x,_) -> x == stack0Head) acc =
dfsInner start (tail stack) finishCounter (gr, acc)
| otherwise =
dfsInner nextEnd (add2Stack++stack) finishCounter (gr V.// [(nextEnd, Node idx True nextLHS nextRHS)], acc)
-- dfsInner gr stack0Head (add2Stack++stack) finishCounter acc
where
(nextStart, nextEnd) = head stack
(Node idx nextExplored nextLHS nextRHS) = gr V.! nextEnd
add2Stack = map (\l -> (nextEnd, l)) nextRHS