Created
January 29, 2013 05:38
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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