#-(and) "

P58 (**) Generate-and-test paradigm

    Apply the generate-and-test paradigm to construct all symmetric,
    completely balanced binary trees with a given number of
    nodes. Example:

    * sym-cbal-trees(5,Ts).

    Ts = [t(x, t(x, nil, t(x, nil, nil)), t(x, t(x, nil, nil), nil)),
    t(x, t(x, t(x, nil, nil), nil), t(x, nil, t(x, nil, nil)))]

    How many such trees are there with 57 nodes? Investigate about how
    many solutions there are for a given number of nodes? What if the
    number is even? Write an appropriate predicate.
"

(load "p54a")
(load "p55")
(load "p56")
(load "p57")

(load "memoize")
(use-package :org.tfeb.hax.memoize)
(import 'alexandria:nconcf)

(load "draw-tree") ; to dump trees in nice ASCII-ART.



(defun generate-balanced-binary-trees-of-height (height next-label)
  ;; When next-label is a constant function,
  ;; generate-balanced-binary-trees-of-height could be memoized.
  (case height
    (0 (list (make-empty-binary-tree)))
    (1 (list (make-binary-tree :label (funcall next-label))))
    (otherwise
     (let ((h-2 (generate-balanced-binary-trees-of-height (- height 2) next-label))
           (h-1 (generate-balanced-binary-trees-of-height (- height 1) next-label)))
       (nconc
        (mapcan (lambda (left)
                  (mapcar (lambda (right)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-1)
        (mapcan (lambda (left)
                  (mapcar (lambda (right)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-2)
        (mapcan (lambda (right)
                  (mapcar (lambda (left)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-2))))))

(memoize-function 'generate-balanced-binary-trees-of-height)




;; The following function generates all the binary trees with a given
;; number of nodes.  Even when memoized, this function is still O(e^n).
;;
;; It can be used to filter the wanted trees, but only with a number
;; of node much less than 57: for 17 nodes it takes on the order of
;; 10 minutes, much more for 18 nodes.

(defun generate-binary-trees-of-nodes (number-of-nodes next-label)
  ;; When next-label is a constant function,
  ;; generate-binary-trees-of-nodes could be memoized.
  (case number-of-nodes
    (0 (list (make-empty-binary-tree)))
    (1 (list (make-binary-tree :label (funcall next-label))))
    (otherwise
     (let ((subtrees (loop
                        :with subtrees = (make-array number-of-nodes)
                        :for i :from 0 :below number-of-nodes
                        :do (setf (aref subtrees i) (generate-binary-trees-of-nodes i next-label))
                        :finally (return subtrees))))
       (loop
          :for left :from 0 :below number-of-nodes
          :for right = (- number-of-nodes left 1)
          :nconc (mapcan (lambda (left-subtree)
                           (mapcar (lambda (right-subtree)
                                     (make-binary-tree :label (funcall next-label)
                                                       :left left-subtree
                                                       :right right-subtree))
                                   (aref subtrees right)))
                         (aref subtrees left)))))))

(memoize-function 'generate-binary-trees-of-nodes)

(defun sym-cbal-trees (n)
  (remove-if-not (lambda (tree)
                   (and (binary-tree-symetric-p tree)
                        (binary-tree-balanced-p tree)))
                 (generate-binary-trees-of-nodes n (constantly 'x))))





#-(and)
(mapcar 'draw-tree
        (generate-binary-trees-of-nodes 5 (let ((n 0)) (lambda () (incf n)))))

#-(and)
(progn
  (defparameter *box*  *ascii-box*)
  (defparameter *line* *ascii-line*))
(progn
  (defparameter *box*  *unicode-box*)
  (defparameter *line* *unicode-line*))

#-(and) "
CL-USER> (mapcar 'draw-tree
                 (generate-binary-trees-of-nodes 3 (let ((n 0)) (lambda () (incf n)))))
(


              ┌─── nil
            ╔═╧═╗
        ┌───╢ 2 ║
        │   ╚═╤═╝
      ╔═╧═╗   └─── nil
  ┌───╢ 3 ║
  │   ╚═╤═╝
╔═╧═╗   └─── nil
╢ 5 ║
╚═╤═╝
  └─── nil



        ┌─── nil
      ╔═╧═╗
  ┌───╢ 4 ║
  │   ╚═╤═╝   ┌─── nil
  │     │   ╔═╧═╗
  │     └───╢ 2 ║
  │         ╚═╤═╝
  │           └─── nil
╔═╧═╗
╢ 6 ║
╚═╤═╝
  └─── nil



        ┌─── nil
      ╔═╧═╗
  ┌───╢ 1 ║
  │   ╚═╤═╝
╔═╧═╗   └─── nil
╢ 7 ║
╚═╤═╝   ┌─── nil
  │   ╔═╧═╗
  └───╢ 1 ║
      ╚═╤═╝
        └─── nil



  ┌─── nil
╔═╧═╗
╢ 8 ║
╚═╤═╝
  │           ┌─── nil
  │         ╔═╧═╗
  │     ┌───╢ 2 ║
  │     │   ╚═╤═╝
  │   ╔═╧═╗   └─── nil
  └───╢ 3 ║
      ╚═╤═╝
        └─── nil



  ┌─── nil
╔═╧═╗
╢ 9 ║
╚═╤═╝   ┌─── nil
  │   ╔═╧═╗
  └───╢ 4 ║
      ╚═╤═╝   ┌─── nil
        │   ╔═╧═╗
        └───╢ 2 ║
            ╚═╤═╝
              └─── nil


)
"


;; (loop for i below 18 collect (cons i  (length (generate-binary-trees-of-nodes i (constantly 'x)))))
;; ((0 . 1)
;;  (1 . 1)
;;  (2 . 2)
;;  (3 . 5)
;;  (4 . 14)
;;  (5 . 42)
;;  (6 . 132)
;;  (7 . 429)
;;  (8 . 1430)
;;  (9 . 4862)
;;  (10 . 16796)
;;  (11 . 58786)
;;  (12 . 208012)
;;  (13 . 742900)
;;  (14 . 2674440)
;;  (15 . 9694845)
;;  (16 . 35357670)
;;  (17 . 129644790))




#-(and)"


CL-USER> (mapcar 'draw-tree (sym-cbal-trees 5))
(

        ┌─── nil
      ╔═╧═╗
  ┌───╢ X ║
  │   ╚═╤═╝   ┌─── nil
  │     │   ╔═╧═╗
  │     └───╢ X ║
  │         ╚═╤═╝
  │           └─── nil
╔═╧═╗
╢ X ║
╚═╤═╝
  │           ┌─── nil
  │         ╔═╧═╗
  │     ┌───╢ X ║
  │     │   ╚═╤═╝
  │   ╔═╧═╗   └─── nil
  └───╢ X ║
      ╚═╤═╝
        └─── nil





              ┌─── nil
            ╔═╧═╗
        ┌───╢ X ║
        │   ╚═╤═╝
      ╔═╧═╗   └─── nil
  ┌───╢ X ║
  │   ╚═╤═╝
╔═╧═╗   └─── nil
╢ X ║
╚═╤═╝   ┌─── nil
  │   ╔═╧═╗
  └───╢ X ║
      ╚═╤═╝   ┌─── nil
        │   ╔═╧═╗
        └───╢ X ║
            ╚═╤═╝
              └─── nil


)
CL-USER>

"




(defun generate-balanced-binary-trees-of-height (height next-label)
  ;; When next-label is a constant function,
  ;; generate-balanced-binary-trees-of-height could be memoized.
  (case height
    (0 (list (make-empty-binary-tree)))
    (1 (list (make-binary-tree :label (funcall next-label))))
    (otherwise
     (let ((h-2 (generate-balanced-binary-trees-of-height (- height 2) next-label))
           (h-1 (generate-balanced-binary-trees-of-height (- height 1) next-label)))
       (nconc
        (mapcan (lambda (left)
                  (mapcar (lambda (right)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-1)
        (mapcan (lambda (left)
                  (mapcar (lambda (right)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-2)
        (mapcan (lambda (right)
                  (mapcar (lambda (left)
                            (make-binary-tree :label (funcall next-label)
                                              :left left
                                              :right right))
                          h-1))
                h-2))))))



(defun safe-aref (vector index)
  (and (<= 0 index (1- (length vector))) (aref vector index)))

(declaim (inline safe-aref))


 (defun generate-balanced-binary-trees-of-nodes (number-of-nodes next-label)
  "Returns all the balanced binary trees that have NUMBER-OF-NODES nodes,
in an vector indexed by the height.
"
  ;; When next-label is a constant function,
  ;; generate-balanced-binary-trees-of-nodes could be memoized.
  (case number-of-nodes
    (0 (vector (list (make-empty-binary-tree))))
    (1 (vector '() (list (make-binary-tree :label (funcall next-label)))))
    (otherwise

     ;; First we get the balanced binary trees with all the number of
     ;; nodes from 0 to (1- number-of-nodes).
     ;;
     ;; Then we will combine two of them each such that the sums of
     ;; number of nodes (plus one for the root) is number-of-nodes,
     ;; but taking care of the balancing.

     (let ((subtrees (loop
                        :with subtrees = (make-array number-of-nodes)
                        :for i :from 0 :below number-of-nodes
                        :do (setf (aref subtrees i)
                                  (generate-balanced-binary-trees-of-nodes i next-label))
                        :finally (return subtrees)))
           ;; The results will be collected in this vector:
           (byheight (make-array (1+ number-of-nodes) :initial-element '())))
       (loop
          :for left :from 0 :below number-of-nodes
          :for right = (- number-of-nodes left 1)

          :for left-byheight  = (aref subtrees left)
          :for right-byheight = (aref subtrees right)

          :do (assert (= (+ left right 1) number-of-nodes))

          ;; Note, we could be smarter here, since given a number of
          ;; nodes, there are minimum and maximum heights of tree you
          ;; can build with them.  For now, we just loop over the
          ;; heights, and check each time the existance of the
          ;; subtrees:

          :do (loop
                 :for left-height    :from 0
                 :for left-subtrees  :across left-byheight
                 :when left-subtrees
                 ;; Find right-subtrees of about the same height as
                 ;; the left ones, and compute their cross product:
                 :do (let ((above (safe-aref right-byheight (1+ left-height)))
                           (level (safe-aref right-byheight     left-height))
                           (below (safe-aref right-byheight (1- left-height))))
                       (when (< (+ left-height 2) (length byheight))
                         (nconcf (aref byheight (+ left-height 2))
                                 (mapcan (lambda (right-subtree)
                                           (mapcar (lambda (left-subtree)
                                                     (make-binary-tree :label (funcall next-label)
                                                                       :left left-subtree
                                                                       :right right-subtree))
                                                   left-subtrees))
                                         above)))
                       (nconcf (aref byheight (+ left-height 1))
                                (mapcan (lambda (right-subtree)
                                          (mapcar (lambda (left-subtree)
                                                    (make-binary-tree :label (funcall next-label)
                                                                      :left left-subtree
                                                                      :right right-subtree))
                                                  left-subtrees))
                                        (append level below))))))
       byheight))))

(memoize-function 'generate-balanced-binary-trees-of-nodes)

;; (map nil (lambda (l) (print l) (mapcar (lambda (n) (princ (draw-tree n)) (terpri)) l))
;;      (generate-balanced-binary-trees-of-nodes 4 (constantly 'x)))

(defun sym-cbal-trees (n)
  (remove-if-not (lambda (tree)
                   (and (binary-tree-symetric-p tree)
                        (binary-tree-balanced-p tree)))
                 (coerce
                  (reduce (function nconc)
                          (generate-balanced-binary-trees-of-nodes n (constantly 'x)))
                  'list)))

;; (loop :for i :below 58  :collect (time (cons i (length (sym-cbal-trees i)))))




;;;; THE END ;;;;
ViewGit