#| -*- Mode:LISP; ReadTable:CL; Fonts:(cptfontb); Base:10 -*- JOSEPHUS.LISP By: Keith M. Corbett Data Structures & Algorithms, Weds. 4:30 pm For: Homework Problem 1 This is a Lisp implementation of the Pascal program example demonstrated in "Data Structures Using Pascal". This helps to prove why we sometimes sacrifice performance for sake of legibility... |# (eval-when (compile) (load "keith;pascal-macros")) ;Need iteration macros ;;; ;;; TNODE -- node structure, contains slots: ;;; ;;; node-name - node label, for pretty printing ;;; node-count - count of descendants ;;; ;;; JOSEPHUS is implemented via an array structure which is used to ;;; create an "almost complete binary tree"... ;;; (DefStruct (tnode) (node-name nil) (node-count 0)) ;;; ;;; Tree/node utility functions ;;; (DefMacro i-th(tree n) "Access the i-th array element, a node. Translates Pascal's 1-based array order to Lisp's 0-based order." `(aref ,tree (sub1 ,n))) (DefMacro node_count (tree i) "Return descendant count of i-th node." `(node-count (i-th ,tree ,i))) (DefMacro node_name (tree i) "Return node label of i-th node." `(node-name (i-th ,tree ,i))) (DeFun dump-tree (tree) "Pretty-print a node tree." (Do-For (i 1 (length tree)) (format t "~%~s) ~s" i (i-th tree i))) (format t "~%")) (defun next-power-of-2 (n) "Calculate next power of 2 >= n." (let( (next-2 1) ) (while (lessp next-2 n) (setq next-2 (times next-2 2))))) ;;; ;;; Main function ;;; (DeFun josephus (n k &aux last-one) "Demonstrate circular removal (by binary tree array) algorithm for a given n, k. Usage: (josephus ), where 1 <= n <= 26, and k is any integer. For example, (josephus 13 9) calculates and returns the item that remains after removing every 9'th element of a 13-element list." (cond ((not (typep n 'integer)) (format t "~%N must be an integer!~%")) ((not (typep n 'integer)) (format t "~%K must be an integer!~%")) ((lessp n 1) (format t "~%N must be >= 1!~%")) ((lessp k 1) (format t "~%K must be >= 1!~%")) ((greaterp n 26) (format t "~%N must be <= 26!~%")) (t (let*( ;;; name list (names (firstn n '(a b c d e f g h i j k l m n o p q r s t u v w x y z))) ;;; number of nodes needed (array dimensions), = n*2 -1 (n-nodes (the integer (sub1 (times n 2)))) ;;; node tree array (tree (the array (make-array n-nodes :element-type 'tnode))) ;;; calculate 2**j, where j is desired height of tree; ;;; the j'th element of array is where the first name goes. (twotomax (the integer (next-power-of-2 n))) ) ;;; confirm what we're doing in writing (format t "~%Remove every ~d~a" k (case k (1 "st") (2 "nd") (3 "rd") (t "th"))) (format t " element from ~s~%" names) ;;; construct 'almost complete binary tree' (do-for (inx 1 n-nodes) (setf (i-th tree inx) (make-tnode))) (do-for (i twotomax n-nodes) (setf (node_name tree i) (car names)) (setf (node_count tree i) 1) (setq names (cdr names))) (do-for (i n (sub1 twotomax)) (setf (node_name tree i) (car names)) (setf (node_count tree i) 1) (setq names (cdr names))) (do-for-downto (i (sub1 n) 1) (setf (node_count tree i) (plus (node_count tree (times i 2)) (node_count tree (add1 (times i 2)))))) (dump-tree tree) ;;; start calculations here... (let*( (p (the integer 1)) (remains (the integer (add1 (mod (sub1 k) (node_count tree 1))))) ) (while (neq (node_count tree 1) 1) (while (greaterp (node_count tree p) 1) (setq p (times p 2)) (when (greaterp remains (node_count tree p)) (setq remains (difference remains (node_count tree p))) (incf p))) ;;; a-ha, got one - print eliminated name. (format t "Eliminated ~a.~%" (node_name tree p)) (let( (q (the integer p)) ) (while (not (zerop q)) (decf (node_count tree q)) (if (= 1 (node_count tree q)) (if (= 1 (node_count tree (times q 2))) (setf (node_name tree q) (node_name tree (times q 2))) (setf (node_name tree q) (node_name tree (add1 (times q 2)))))) (setq q (quotient q 2)))) (setq remains k) (unless (oddp p) (incf p)) (while (and (greaterp remains (node_count tree p)) (neq p 1)) (decf remains (node_count tree p)) (while (and (oddp p) (neq p 1)) (setq p (quotient p 2))) (if (neq p 1) (incf p))) ;End while (if (= p 1) (setq remains (add1 (mod (sub1 remains) (node_count tree p))))) ) ;End while root count <> 1 (setq last-one (node_name tree 1)) ) ;End long LET (format t "~%Only ~a remains.~%" last-one) last-one) ;End long LET -- return final value? ;End JOSEPHUS )))