git.fiddlerwoaroof.com
Raw Blame History
(defpackage :fwoar.lisp-sandbox.kruskal
  (:use :cl )
  (:export ))
(in-package :fwoar.lisp-sandbox.kruskal)

(defclass disjoint-set ()
  ((%size :accessor ds-size :initform 1)
   (%parent :accessor ds-parent :initform nil)))

(defmethod initialize-instance :after ((ds disjoint-set) &key)
  (setf (ds-parent ds) ds))
(defmethod print-object ((o disjoint-set) s)
  (print-unreadable-object (o s :type t :identity t)
    (format s "size: ~d parent: ~s"
            (ds-size o)
            (unless (eql (ds-parent o) o)
              (ds-parent o)))))

(defun ds-find (ds)
  (if (eql (ds-parent ds)
           ds)
      ds
      (setf (ds-parent ds)
            (ds-find (ds-parent ds)))))


(defun ds-union (x y)
  (let ((x (ds-find x))
        (y (ds-find y)))
    (unless (eql x y)
      (when (< (ds-size x)
               (ds-size y))
        (rotatef x y))
      (setf (ds-parent y) x
            (ds-size x) (+ (ds-size x)
                           (ds-size y))))
    x))

(defun kruskal (edges)
  (let ((node-labels (make-hash-table)))
    (labels ((l (it)
               (alexandria:ensure-gethash it node-labels
                                          (make-instance 'disjoint-set)))
             (k-step (forest edges)
               (destructuring-bind ((s e . edge-rest) . rest) edges
                 (let ((ds-s (l s))
                       (ds-e (l e)))
                   (values (if (eql (ds-find ds-s)
                                    (ds-find ds-e))
                               forest
                               (progn
                                 (ds-union ds-s ds-e)
                                 (cons (list* s e edge-rest) forest)))
                           rest)))))
      (loop for (s e) in edges do
        (l s)
        (l e))
      (loop for (forest %edges) = (multiple-value-list (k-step () edges))
              then (multiple-value-list (k-step forest %edges))
            while %edges
            finally (return (reverse forest))))))

#|
|---+---+---+---|
| a | b | c | d |
|---+---+---+---|
| e | f | g | h |
|---+---+---+---|
| j | k | l | m |
|---+---+---+---|
| n | o | p | q |
|---+---+---+---|

|---+---+---+---|
| a | b   c   d |
|   +   +---+   |
| e   f   g | h |
|---+---+   +---|
| i   j   k   l |
|   +---+---+   |
| m | n   o   q |
|---+---+---+---|
|#
(defun grid-graph (max)
  (flet ((w ()
           (random max)))
    (stable-sort `((a b ,(w)) (a e ,(w)) ;; (a f ,(w))
                   (b c ,(w)) (b f ,(w)) ;; (b g ,(w))
                   (c d ,(w)) (c g ,(w)) ;; (c h ,(w))
                   #|      |# (d h ,(w)) ;;
                   (e f ,(w)) (e j ,(w)) ;; (e k ,(w))
                   (f g ,(w)) (f k ,(w)) ;; (f l ,(w))
                   (g h ,(w)) (g l ,(w)) ;; (g m ,(w))
                   #|      |# (h m ,(w))
                   (j k ,(w)) (j n ,(w)) ;; (j o ,(w))
                   (k l ,(w)) (k o ,(w)) ;; (k p ,(w))
                   (l m ,(w)) (l p ,(w)) ;; (l q ,(w))
                   #|      |# (m q ,(w))
                   (n o ,(w))
                   (o p ,(w))
                   (p q ,(w)))
                 '<
                 :key #'third)))

(defun grid-edges (w h)
  (remove-if-not (lambda (it)
                   (destructuring-bind ((s-x s-y)
                                        (e-x e-y))
                       it
                     (and (< s-x w)
                          (< e-x w)
                          (< s-y h)
                          (< e-y h))))
                 (loop for x below w
                       append (loop for y below h
                                    append (list (list (list x y)
                                                       (list x (1+ y)))
                                                 (list (list x y)
                                                       (list (1+ x) y)))))))

(defun print-grid (grid edge-map)
  (let ((edges (make-hash-table :test #'equal)))
    (loop
      for (s e) in edge-map do
        (push e (gethash s edges)))
    (princ #\+)
    (loop repeat (1- (* (array-dimension grid 1) 4)) do
      (princ "-"))
    (princ #\+)
    (terpri)
    (loop
      for x below (array-dimension grid 0) do
        (princ #\|)
        (loop for y below (array-dimension grid 1)
              do
                 (format t " ~a " (aref grid x y))
              when (< y (1- (array-dimension grid 1)))
                do (princ (if (member (list x (1+ y))
                                      (gethash (list x y)
                                               edges)
                                      :test #'equal)
                              #\space
                              #\|)))
        (princ #\|)
        (terpri)
      when (< x (1- (array-dimension grid 0))) do
        (princ #\|)
        (loop for y below (array-dimension grid 1)
              do
                 (princ
                  (if (member (list (1+ x) y)
                              (gethash (list x y)
                                       edges)
                              :test #'equal)
                      "   "
                      "---"))
              when (< y (1- (array-dimension grid 1)))
                do (princ #\+))
        (princ #\|)
        (terpri))
    (princ #\+)
    (loop repeat (1- (* (array-dimension grid 1) 4)) do
      (princ "-"))
    (princ #\+)
    (terpri)))

(defun fully-connected (n max-weight)
  (flet ((symbolicate (it)
           (coords->symbol (list (floor it (1+ (floor (sqrt n))))
                                 (mod it (1+ (floor (sqrt n)))))
                           n)))
    (let ((nodes (loop for x below n
                       collect x)))
      (loop
        for (h . tail) on nodes
        append (loop for it in tail
                     collect (list (symbolicate h)
                                   (symbolicate it)
                                   (random max-weight)))))))

(defun symbol->coords (sym side-len)
  (let* ((v (- (char-code (elt (symbol-name sym) 0))
               #.(char-code #\A)))
         (v (if (> v 8)
                (1- v)
                v)))
    (list (floor v side-len)
          (mod v side-len))))

(defun coords->symbol (coords side-len)
  (let ((it (+ (* side-len (elt coords 0))
               (elt coords 1))))
    (intern
     (string
      (code-char
       (+ #.(char-code #\A)
          (if (>= it 8)
              (1+ it)
              it)))))))

(defun ->graph (edges &optional (s t))
  (format s "graph {~%~{~:@{~2t~a -- ~a [label=\"~a\"]~%~}~}~&}~%"
          edges))