如何使用 test.check 生成随机图?
How can I generate random graphs with 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 看不到正在生成的邻接列表。所以我担心这会搞砸收缩逻辑。
是否有避免这两个陷阱的解决方案?
这是另一种方法:
(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
文档字符串:
Note that this function is a dev helper and is not meant to be used to build other generators.
但您可以使用 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 #{}}
我正在尝试生成邻接表形式的随机图以用于生成测试。示例图为:
{: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 看不到正在生成的邻接列表。所以我担心这会搞砸收缩逻辑。
是否有避免这两个陷阱的解决方案?
这是另一种方法:
(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
文档字符串:
Note that this function is a dev helper and is not meant to be used to build other generators.
但您可以使用 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 #{}}