如何使用test.check生成随机图?

时间:2018-10-31 12:49:51

标签: clojure test.check

出于生成测试的目的,我正在尝试以邻接表的形式生成随机图。一个示例图为:

{:a #{:a :b}, :b #{:a :b}}

(邻接列表是按集合实现的。)

我的第一个想法是:

(def vertex-gen (tcgen/fmap (comp keyword str) tcgen/char-alpha-numeric))

(def random-graph-gen-1
  (tcgen/let [vertices (tcgen/set vertex-gen {:min-elements 1})]
             (tcgen/map (tcgen/elements vertices)
                        (tcgen/set (tcgen/elements vertices)))))

({{min-elements 1}是必需的,因为tcgen/elements不适用于空集。)

但是,这样会冒生成诸如

之类的图形的风险
{:a #{:a :b}}

其中:b是为:a的邻接表随机选择的,但没有为图形本身选择的。因此:a有一个不存在的邻居。

另一个选择是

(def random-graph-gen-2
  (tcgen/let [vertices (tcgen/set vertex-gen)]
             (->> vertices
                  (map #(->> vertices
                             (tcgen/elements)
                             (tcgen/set)
                             (tcgen/generate)
                             (vector %)))
                  (into {}))))

,它遍历所有顶点并为每个顶点显式生成一个随机邻接表。这样可以确保所有顶点都将出现在图形中,但不利之处是test.check不会看到生成的邻接表。因此,我担心这会破坏缩小的逻辑。

有没有避免这两个陷阱的解决方案?

1 个答案:

答案 0 :(得分:2)

这是另一种方法:

(def graph-gen
  (gen/let [vertices (gen/set vertex-gen {:min-elements 1})
            edges (-> vertices
                      (gen/elements)
                      (gen/set)
                      (gen/vector (count vertices)))]
    (zipmap vertices edges)))

这引入了第二个生成器,该生成器为每个顶点生成一组边,然后将顶点和边压缩为一个贴图。

在第二个示例中使用generate是引入不受整个生成器大小限制的随机性。来自generate文档字符串:

  

请注意,此函数是开发者的助手,并不用于构建其他生成器。

但是您可以使用generate从生成器中获取不同大小的样本:

(gen/generate graph-gen 2)
=> {:F #{}, :e #{}, :S #{:e}}
(gen/generate graph-gen 10)
=>
{:L #{:n :7 :8 :H},
 :n #{:L :n :7 :C :8 :b :H :V},
 :7 #{:L :7 :C :8 :9 :b},
 :C #{:L :9 :V},
 :8 #{:L :n :7 :C :8 :9 :b :V},
 :9 #{:L :b :a},
 :b #{:n :V},
 :H #{:a},
 :V #{:n :C :b :H},
 :a #{}}