Haskell中图的传递闭包

时间:2015-07-16 14:06:25

标签: haskell set

以下程序的目的是关系的传递闭包(作为一组有序对 - 图形)和关于该关系的有序对的成员资格的测试。

我试图通过使用Data.Set代替列表来提高程序的效率,并消除丢失对中生成的冗余。

我想知道:

  1. 如何使用QuickCheck验证其正确性;
  2. 如果可能的话,如何计算程序的效率,或者 它与问题的类似解决方案相比如何(例如 Transitive closure from a list)。
  3. 任何批评和建议都将受到赞赏。

    import Data.Set as S
    import Data.Foldable as F (foldMap)
    
    data TruthValue = F | U | T deriving (Show,Eq)
    
    isMemberOfTransitiveGraph :: Ord t => (t, t) -> Set (t, t) -> TruthValue
    (x,y) `isMemberOfTransitiveGraph` gr
      | S.member (x,y) closure = T -- as suggested by user5402
      | S.member (y,x) closure = F -- as suggested by user5402
      | otherwise = U
      where
      closure = transitiveClusureOfGraph gr -- as suggested by user5402
    
    transitiveClusureOfGraph :: Ord a => Set (a, a) -> Set (a, a)
    transitiveClusureOfGraph gr = F.foldMap (transitiveClosureOfArgument gr) domain
      where
      domain = S.map fst gr
    
    transitiveClosureOfArgument :: Ord a => Set (a, a) -> a -> Set (a, a)
    transitiveClosureOfArgument gr x = S.map ((,) x) $ recursiveImages gr (S.singleton x)
    
    recursiveImages :: Ord a => Set (a, a) -> Set a -> Set a
    recursiveImages gr imgs = f gr imgs S.empty
      where
      f :: Ord a => Set (a, a) -> Set a -> Set a -> Set a
      f gr imgs acc
        | S.null imgs = acc
        | otherwise = f gr (newImgs S.\\ acc) (S.union newImgs acc)
        where
        newImgs = F.foldMap (imaginsOf gr) imgs
    
    imaginsOf :: (Ord b, Eq a) => Set (a, b) -> a -> Set b
    imaginsOf gr arg =  S.foldr (\(a,b) acc -> if a == arg then S.insert b acc else acc) S.empty gr
    

    **

    示例1

    **

    someLessThan = S.fromList [("1","2"),("1","4"),("3","4"),("2","8"),("3","5"),("4","7"),("4","8"),("3","9")]
    
    > transitiveClusureOfGraph someLessThan
    > fromList [("1","2"),("1","4"),("1","7"),("1","8"),("2","8"),("3","4"),("3","5"),("3","7"),("3","8"),("3","9"),("4","7"),("4","8")]
    
    a `isLessThan` b = (a,b) `isMemberOfTransitiveGraph`  someLessThan
    > "1" `isLessThan` "8"
    > T
    > "8" `isLessThan` "1"
    > F
    > "1" `isLessThan` "9"
    > U
    > "9" `isLessThan` "1"
    > U
    

    **

    示例2

    **

    someTallerThan = S.fromList  [("Alexandre","Andrea"),("Andrea","John"),("George","Frank"),("George","Lucy"),("John","Liza"),("Julia","Lucy"),("Liza","Bob"),("Liza","Frank")]
    
    > transitiveClusureOfGraph someTallerThan
    > fromList [("Alexandre","Andrea"),("Alexandre","Bob"),("Alexandre","Frank"),("Alexandre","John"),("Alexandre","Liza"),("Andrea","Bob"),("Andrea","Frank"),("Andrea","John"),("Andrea","Liza"),("George","Frank"),("George","Lucy"),("John","Bob"),("John","Frank"),("John","Liza"),("Julia","Lucy"),("Liza","Bob"),("Liza","Frank")]
    
    a `isTallerThan` b = (a,b) `isMemberOfTransitiveGraph` someTallerThan
    > "Alexandre" `isTallerThan` "Frank"
    > T
    > "Frank" `isTallerThan` "Alexandre"
    > F
    > "Alexandre" `isTallerThan` "George"
    > U
    > "George" `isTallerThan` "Alexandre"
    > U
    

    **

    实施例3

    **

    incomeIsLessOrEqualThan = S.fromList [("Bob","Liza"),("Liza","Tom"),("Tom","Bob"),("Tom","Mary"),   ("Tom","Tom")]
    
    > S.filter (\(a,b) -> a /= b) $ transitiveClusureOfGraph incomeIsLessOrEqualThan
    
    > fromList [("Bob","Liza"),("Bob","Mary"),("Bob","Tom"),("Liza","Bob"),("Liza","Mary"),("Liza","Tom"),("Tom","Bob"),("Tom","Liza"),("Tom","Mary")]
    

1 个答案:

答案 0 :(得分:1)

一些意见:

  1. Quickcheck测试的一些想法:

    • 创建一个随机连接图并验证每对点是否在传递闭包中。
    • 验证对于任何随机图,传递闭包的传递闭包只与传递闭包一次相同。
    • 验证您的代码是否与其他实现返回相同的答案(例如来自fgl库。)
  2. 然而,当我查看fgl库时,我发现他们只是使用固定的图来测试它们的路径查询功能。然后他们确切地知道所有测试的答案应该是什么。

    另一个想法是解决ACM(编程竞争)问题,该问题涉及查找图的传递闭包,并在该解决方案中使用您的代码。 Timuscodeforces都接受Haskell程序。

    1. isMemberOfTransitiveGraph中,您有共同的子表达式transitiveClusureOfGraph gr。现在GHC可以(并且应该)检测到这一点并将其排除在外,这样它就不会被评估两次,但它并不总能这样做。此外,作为一名翻译,ghci不会执行常见的子表达式消除。因此,鉴于transitiveClusureOfGraph是一项昂贵的操作,您应该编写此函数,如
    2. 这样:

      isMemberOfTransitiveGraph (x,y) gr
        | S.member (x,y) closure = T
        | S.member (y,x) closure = F
        | otherwise              = U
        where
          closure = transitiveClusureOfGraph gr in
      
      1. 此外,计算整个图的传递闭包是 一种昂贵的方法来确定特定货币对是否在关闭中。 实现isMemberOfTransitiveClosure的更好方法是简单地 在该对的一个成员上执行深度优先搜索,直到您a)找到另一个元素或b)填写连接的组件而不查找其他元素。否则,您正在对其他连接组件执行大量工作,这与您尝试回答的问题无关。

      2. 如果您真的关心效率,请将节点类型限制为Int,并使用Data.IntSet甚至Data.BitSet来表示节点集。