以下程序的目的是关系的传递闭包(作为一组有序对 - 图形)和关于该关系的有序对的成员资格的测试。
我试图通过使用Data.Set代替列表来提高程序的效率,并消除丢失对中生成的冗余。
我想知道:
任何批评和建议都将受到赞赏。
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
**
**
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
**
**
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
**
**
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")]
答案 0 :(得分:1)
一些意见:
Quickcheck测试的一些想法:
然而,当我查看fgl库时,我发现他们只是使用固定的图来测试它们的路径查询功能。然后他们确切地知道所有测试的答案应该是什么。
另一个想法是解决ACM(编程竞争)问题,该问题涉及查找图的传递闭包,并在该解决方案中使用您的代码。 Timus和codeforces都接受Haskell程序。
isMemberOfTransitiveGraph
中,您有共同的子表达式transitiveClusureOfGraph gr
。现在GHC可以(并且应该)检测到这一点并将其排除在外,这样它就不会被评估两次,但它并不总能这样做。此外,作为一名翻译,ghci不会执行常见的子表达式消除。因此,鉴于transitiveClusureOfGraph
是一项昂贵的操作,您应该编写此函数,如这样:
isMemberOfTransitiveGraph (x,y) gr
| S.member (x,y) closure = T
| S.member (y,x) closure = F
| otherwise = U
where
closure = transitiveClusureOfGraph gr in
此外,计算整个图的传递闭包是
一种昂贵的方法来确定特定货币对是否在关闭中。
实现isMemberOfTransitiveClosure
的更好方法是简单地
在该对的一个成员上执行深度优先搜索,直到您a)找到另一个元素或b)填写连接的组件而不查找其他元素。否则,您正在对其他连接组件执行大量工作,这与您尝试回答的问题无关。
如果您真的关心效率,请将节点类型限制为Int
,并使用Data.IntSet
甚至Data.BitSet
来表示节点集。