Как я могу сгенерировать случайные графики с помощью 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 не видит создаваемые списки смежности. Так что я опасаюсь, что это испортит логику сжатия.

Есть ли решение, позволяющее избежать обеих этих ловушек?

2
0
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот еще один способ сделать это:

(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 #{}}

Другие вопросы по теме