Thank you for the example & pointing me to the official name of this
problem, Mark! Having done some more digging I found a solution which
still uses the "canonical nodes" idea (root nodes of component
clusters/subtrees) but does structure it differently and compresses
the paths of new nodes as they're being added. For additions it's
~2.5x faster than your version, but ~1.25x slower for lookups
(compared to yours)... but altogether still much faster than what I
had previously, great!

;; based on WQUPC section of
http://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf

(defn root
  "Takes a node map `nodes` and node `x`, returns root node of `x`."
  [nodes x]
  (loop [x x, nx (nodes x)] (if (= x (or nx x)) x (recur nx nx))))

(defn root*
  "Like `root`, but compresses path while walking tree.
  Returns [updated-tree root-node]."
  [nodes x]
  (loop [nodes nodes, x x, tx (nodes x x)]
    (if (= x tx)
      [nodes x]
      (let [tx' (nodes tx tx)
            nodes' (assoc nodes x tx')]
        (recur nodes' tx' tx')))))

(defn same-as?
  "Takes a graph structure and two node keys, returns true if keys
  have same root."
  [{:keys [nodes]} p q] (= (root nodes p) (root nodes q)))

(defn unite
  "Takes a graph structure and two keys and joins them under a common
  root node. Compresses paths. Returns updated graph."
  [{:keys [nodes depth] :or {nodes {} depth {}} :as g} [p q]]
  (let [[nodes i] (root* nodes p)
        [nodes j] (root* nodes q)
        di (depth i 1)
        dj (depth j 1)
        [i j] (if (< di dj) [i j] [j i])
        g (assoc g :nodes (assoc nodes i j))]
    (if (= i j) g
        (assoc g :depth (-> depth (dissoc i) (assoc j (+ di dj)))))))

(time
 (dotimes [i 100000]
   (def g
     (reduce unite {} '[[x d] [a b] [b d] [e f] [d y] [c y] [y b] [g e]]))))
;; "Elapsed time: 1399.076 msecs"

(time (dotimes [i 100000] (same-as? g 'x 'y)))
;; "Elapsed time: 53.779 msecs"

K.

On 16 January 2014 20:12, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> Whoops, typo, should have been:
> "(if (<= (count component1) (count component2))"
>
> Corrected:
>
> (defn register-same-as [{:keys [keys->canonical canonical->components]
>                          :as component-graph}
>                         key1 key2]
>   (let [canonical1 (keys->canonical key1 key1)
>         canonical2 (keys->canonical key2 key2)]
>     (if (= canonical1 canonical2) component-graph   ; already the same
>       (let [component1 (canonical->components canonical1 [canonical1])
>             component2 (canonical->components canonical2 [canonical2])
>             ;swap if necessary so component1 is the smaller component
>             [canonical1 canonical2 component1 component2] (if (<= (count
> component1) (count component2))
>
>                                                             [canonical1
> canonical2 component1 component2]
>                                                             [canonical2
> canonical1 component2 component1])
>             new-keys->canonical (into keys->canonical (for [item component1]
> [item canonical2]))
>             new-canonical->components (-> canonical->components
>                                         (dissoc canonical1)
>                                         (assoc canonical2 (into component2
> component1)))]
>         {:keys->canonical new-keys->canonical, :canonical->components
> new-canonical->components}))))
>
>
>
> --
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.



-- 
Karsten Schmidt
http://postspectacular.com | http://toxiclibs.org | http://toxi.co.uk

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to