Skip to content

Instantly share code, notes, and snippets.

@NicMcPhee
Created January 29, 2013 05:38
Show Gist options
  • Save NicMcPhee/4662080 to your computer and use it in GitHub Desktop.
Save NicMcPhee/4662080 to your computer and use it in GitHub Desktop.
Solution to 4Clojure's Graph Connectivity problem (http://www.4clojure.com/problem/91). This was hard enough that I did it in Eclipse with Counterclockwise (with some unit tests, etc.), and then turned it into one big fn at the end to paste into 4Clojure.
(ns four-clojure-problems.graph-connectivity)
(use 'clojure.test)
;; Take a connection map (maps nodes to sets of
;; nodes that are all the nodes the key node
;; connects to) and an edge, and returns a new,
;; updated connection map.
(defn process-connection [connections-map edge]
(let [a (first edge)
b (second edge)
a-connections (connections-map a)
b-connections (connections-map b)
merged-connections (clojure.set/union a-connections b-connections)
nodes (keys connections-map)]
(zipmap nodes
(map #(if (contains? merged-connections %1)
merged-connections
(connections-map %1))
nodes)))
)
;; Take a graph and return a map that maps nodes to
;; sets of nodes that represent all the nodes the key
;; node is connected to.
(defn build-all-connections [graph]
(let [nodes (set (flatten (vec graph)))
initial-connections (reduce (fn [m n] (assoc m n #{n})) {} nodes)]
(reduce process-connection initial-connections graph)))
(is (= {:a #{:a :b}
:b #{:a :b}}
(build-all-connections #{[:a :b]})))
(is (= {:a #{:a :b :c :d :e}
:b #{:a :b :c :d :e}
:c #{:a :b :c :d :e}
:d #{:a :b :c :d :e}
:e #{:a :b :c :d :e}
:x #{:x :y}
:y #{:x :y}}
(build-all-connections #{[:a :b] [:b :c] [:c :d]
[:x :y] [:d :a] [:b :e]})))
;; I'm going to have a map that maps nodes to the set of nodes we know they're
;; connected to. Then for each edge I'm going to merge the sets associated with
;; the nodes on that edge, and then map all nodes in the resulting set to that
;; new set since they're now connected to everything in that set. Then at the
;; end I just need to see if the set of nodes any node maps to is the full set
;; since if any node is connected to everyone then every node should be connected
;; to everyone.
(defn connected? [graph]
(let [nodes (set (flatten (vec graph)))
;; A map from nodes to sets of nodes that represent
;; all the nodes the key node is connected to.
all-connections (build-all-connections graph)]
(= nodes (second (first all-connections)))))
;;; Turn the 4Clojure tests into unit tests
(is (= true (connected? #{[:a :a]})))
(is (= true (connected? #{[:a :b]})))
(is (= false (connected? #{[1 2] [2 3] [3 1]
[4 5] [5 6] [6 4]})))
(is (= true (connected? #{[1 2] [2 3] [3 1]
[4 5] [5 6] [6 4] [3 4]})))
(is (= false (connected? #{[:a :b] [:b :c] [:c :d]
[:x :y] [:d :a] [:b :e]})))
(is (= true (connected? #{[:a :b] [:b :c] [:c :d]
[:x :y] [:d :a] [:b :e] [:x :a]})))
(fn [graph]
(let [nodes (set (flatten (vec graph)))
initial-connections (reduce (fn [m n] (assoc m n #{n})) {} nodes)
process-connection (fn [connections-map edge]
(let [a (first edge)
b (second edge)
a-connections (connections-map a)
b-connections (connections-map b)
merged-connections (clojure.set/union a-connections b-connections)]
(zipmap nodes
(map #(if (contains? merged-connections %1)
merged-connections
(connections-map %1))
nodes))))
build-all-connections (fn [graph]
(reduce process-connection initial-connections graph))
all-connections (build-all-connections graph)]
(= nodes (second (first all-connections)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment