用于生成无偏图的快速检查的任意实例

时间:2016-04-04 09:01:21

标签: haskell testing graph geometry quickcheck

module Main where

import Test.QuickCheck
import Data.Set as Set    

data Edge v = Edge {source :: v, target :: v}
                  deriving (Show,Eq,Ord)

data Graph v = Graph {nodes :: Set v, edges :: Set (Edge v)}
               deriving Show

instance Arbitrary v => Int-> Arbitrary (Edge v) where
    arbitrary = sized aux 
        where aux n = do s <- arbitrary
                         t <- arbitrary `suchThat` (/= s)
                         return $ Edge {source = s, target = t}


instance (Ord v, Arbitrary v) => Arbitrary (Graph v) where
    arbitrary = aux `suchThat` isValid
        where aux = do ns <- arbitrary 
                       es <- arbitrary 
                       return $ Graph {nodes = fromList ns, edges = fromList es}

该实例的当前定义是生成边缘较少的图形,如何更改它以使其偏差较小且满足这两个函数? :

- |功能&#39;是DAG&#39;测试图表是否为非循环。

isDAG :: Ord v => Graph v -> Bool
isDAG g = isValid g && all nocycle (nodes g)
    where nocycle v = all (\a -> v `notMember` reachable g a) $ Set.map target (adj g v)

- |功能&#39; isForest&#39;测试一个有效的DAG是一个florest(一组树),换句话说, - 如果每个节点(顶点)最多有一个相邻的。

isForest :: Ord v => DAG v -> Bool
isForest g = isDAG g && all (\v -> length (adj g v) <= 1) (nodes g)

2 个答案:

答案 0 :(得分:3)

首先,您必须弄清楚如何构建满足这些属性的图形。

DAG:如果您的节点允许某些排序,并且对于每个边(u,v),您有u < v,那么该图表是非循环的。这种排序可以是任何排序,因此您可以在图中的节点集上制造任意排序。

Forest:如果您的图表没有边缘,则此属性非常满意。最初,您可以添加源为任何节点的任何边。如果添加边缘,请从剩余的可用节点中删除该边缘的源。

我想最重要的问题是如何将其转换为代码。 QuickCheck提供了许多组合器,尤其是。从列表中选择,有或没有替换,各种大小等。

instance (Ord v, Arbitrary v) => Arbitrary (Graph v) where 
  arbitrary = do 
    ns <- Set.fromList <$> liftA2 (++) (replicateM 10 arbitrary) arbitrary

首先生成一组随机节点。

    let ns' = map reverse $ drop 2 $ inits $ Set.toList ns 

对于每个节点,这将计算比该节点“更大”的(非空)节点集。这里“更大”仅仅意味着根据列表中元素顺序引起的任意排序。这可以获得DAG属性。

    es <- sublistOf ns' >>= 
            mapM (\(f:ts) -> Edge f <$> elements ts)

然后,您将获得该列表的随机子列表(它将获取林属性),并且对于该随机子列表中的每个元素,您创建一个从该集合中的“最大”节点指向“较小”的边缘”。

    return $ Graph ns (Set.fromList es) 

然后你就完成了!测试如下:

main = quickCheck $ forAll arbitrary (liftA2 (&&) (isDAG :: Graph Integer -> Bool) isForest)

答案 1 :(得分:0)

构造图形的一种自然方式是归纳,一次添加一个节点。然后很容易确保所需的属性成立:

  • 如果对于每个添加的节点,其边缘仅指向现有节点(而不是指向另一个节点),我们确保DAG属性。
  • 如果一个节点最多有一个边缘,我们确保林属性。 (由于你没有提供adj功能,所以不清楚是否&#34;林&#34;你的意思是最多只有一个边缘来自 >节点或节点。)

因此,生成此类图表的过程如下:

  1. 生成随机节点列表。
  2. 通过逐个添加图表来构建图表。对于每个节点,要么将随机边缘添加到已添加的节点之一,要么添加边缘(随机决定)。
  3. 这里的主要因素是决定是否添加边缘。通过调整此参数,您可以在林中获得更多或更少的树。一种选择是使用frequency