如何加快此图算法的速度?

时间:2014-05-21 09:51:03

标签: algorithm haskell graph

完全披露:我将此作为hackerrank.com challenge的一部分撰写。

作为我的算法的一部分,我必须获取对的列表并生成集合列表。 每对代表两个节点之间的链接,每个Set代表彼此链接的所有节点(直接和间接)。

这些对是列表的形式,因为这是它们的生成方式,我不想浪费时间将它们重新装入元组中。此外,无可辩驳的模式消除了大部分开销。

import Data.List
import Data.Maybe
import qualified Data.IntSet as IS (fromList)

mkgroups :: [[Int]] -> [IntSet]
mkgroups = foldl' add2gc []
  where
    -- if a group is not found, create a new group
    add2gc [] is = [IS.fromList is]
    -- for the current group,
    add2gc (g:gc) ~is@[i1, i2]
      -- if either mates in group, add to group.
      | i1 `IS.member` g = merge2gc i2 g gc
      | i2 `IS.member` g = merge2gc i1 g gc
      -- otherwise try next group
      | otherwise = g : add2gc gc is
    -- merge other inmate to appropriate group
    merge2gc i g gc
      -- in original group, return original group
      | i `IS.member` g = g:gc
      | otherwise = case part (IS.member i) gc of
      -- in any other group, merge that group with this.
        (Just g',gs) -> (IS.union g g') : gs
      -- otherwise add innmate to this group
        _ -> (IS.insert i g):gc

我需要一个能够返回第一个匹配项和所有非匹配实体列表的函数,而不是partition。使用此函数而不是分区将算法从O(n ^ 2)更接近O(n * log n)分摊(并使我的运行时间降低15%)

part p as = go as []
  where 
    go []     ps  = (Nothing, ps)
    go (a:as) ps = if p a
      then (Just a, ps ++ as)
      else go as (a:ps)

我还尝试为作业选择最佳的数据结构。最初这些组是[[Int]]并且实际上工作得很好(因为移动到[IntSet]仅使运行时间提高了30%)。

尽管如此,我还是需要更快。我的最后一次测试是在8秒时超时,我无法看到测试背后的输入数据。它可能会遇到最坏情况的路径,甚至只是一大堆数据。我已经尝试了我所知道的所有事情来加快速度,即使使用我的知识,内部输入列表中总会有2个元素使一个匹配无可辩驳。目前,我正在尝试使用分析数据重建我的本地GHC安装和软件包,以尝试深入分析代码。

SO Haskell大师有什么东西可以看到或想到我还没有尝试过吗?


编辑:我认为我目前拥有的算法是对我所考虑的目标的合理解释,尽管它在设置插入时遭受了大量复制。因此,感谢@fizruk等。 al。,我使用标准库中的Data.Graph重新实现。

import Data.Tree as T
import Data.Graph as G

mkgroups :: Int -> [(Int,Int)] -> Forest Vertex
mkgroups n = filter ((>1) . length . T.flatten) . G.components . G.buildG (0,n)

这很好地完成了工作,并且运行得更快(大约4倍)。感谢所有帮助过的人。我决定给@ phil_20686答案,因为在阅读了图书馆资料后,他的回答似乎与图书馆实际上的答案最接近。

2 个答案:

答案 0 :(得分:2)

union-find算法被设计为在一个密切相关的问题上快速血腥。 Hackage search表明有几个包实现了这个算法,你可能有兴趣检查一下。但是,正如下面的评论所讨论的那样,措辞密切相关"在这种情况下,实际上是负载处理:union-find解决了一个稍微困难的问题,因此实际上并不是最佳的。也就是说,如果图中有两个节点,联合查找结构将能够最快地告诉您它们是否属于同一个连接组件。

答案 1 :(得分:1)

构建适当的数据结构后,您可以使用广度优先或深度优先搜索在线性时间内解决此问题。

然而,在时间或内存中构建数据结构不一定是平凡的。例如。你可以将ever元素放入一对多图(一个用于传入,一个用于传出)。然后,只需转到相应的键,然后获取所有连接的节点,就可以轻松实现BFS。如果您将其从地图中删除,则可以确保您不会重复自己。

由于地图以值(节点)的数量在线性时间内构建,并且BFS在边数上是线性的,因此性能将为O(E + V)。