Created
December 18, 2013 11:29
-
-
Save reborg/8020830 to your computer and use it in GitHub Desktop.
An implementation of the travel salesman problem as clojure lazy seq
This file contains 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
;; An implementation of a lazy seq that given a graph in the form of a list | |
;; of edges (e.g. ["AB" "BC" "CD"] can be de-queued of any number | |
;; of valid paths (in the above example ["AB" "BC" "CD" "ABC" "BCD"] only). | |
;; Returns nils for acyclic graphs after the number of all | |
;; available paths have been exhausted. So if you take 100 from the above, | |
;; the returned collection will have nils after 5 elements. For cyclic graphs | |
;; it will follow the cycles infintely. To prevent infinte loops, it will | |
;; exit after a threshold anyway. | |
;; The approach is definitely brute-force. Optimisations can be done by | |
;; introducing weight for edges and operating on less costly paths first. | |
;; See http://en.wikipedia.org/wiki/Travelling_salesman_problem | |
(defn join [r1 r2] | |
"Join routes when r2 starts where r1 ends." | |
(if (and (not (nil? r1)) (not (nil? r2)) (connected? r1 r2)) | |
(str r1 (.substring r2 1)))) | |
(defn all [c1 c2] | |
"Produce a list of connected paths given an initial set of connecting points. | |
Invoke first with c1=[] and c2=[\"AB\" \"BC\"] for example." | |
(lazy-seq | |
(let [c1+c2 (concat c1 c2) | |
comb (mapcat #(map (fn [r] [% r]) c1+c2) c1+c2) | |
connected (map #(join (first %) (last %)) comb)] | |
(concat c2 (all c2 connected))))) | |
(defn take* [n coll] | |
"Take a batch of items from coll until the required number | |
of distinct not-nil items (or the threshold) has been reached." | |
(loop [i 10e5] | |
(let [fetch (distinct (remove nil? (clojure.core/take i coll)))] | |
(if (and (< (count fetch) n) (< i 10e12)) | |
(recur (+ 10e5 i)) | |
(clojure.core/take n fetch))))) | |
(defn take [n edges] | |
"Fetch first n valid paths starting from the initial set of edges" | |
(take* n (all '() edges))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment