295 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
(in-package :alexandria)
 | 
						|
 | 
						|
(declaim (inline clamp))
 | 
						|
(defun clamp (number min max)
 | 
						|
  "Clamps the NUMBER into [min, max] range. Returns MIN if NUMBER is lesser then
 | 
						|
MIN and MAX if NUMBER is greater then MAX, otherwise returns NUMBER."
 | 
						|
  (if (< number min)
 | 
						|
      min
 | 
						|
      (if (> number max)
 | 
						|
          max
 | 
						|
          number)))
 | 
						|
 | 
						|
(defun gaussian-random (&optional min max)
 | 
						|
  "Returns two gaussian random double floats as the primary and secondary value,
 | 
						|
optionally constrained by MIN and MAX. Gaussian random numbers form a standard
 | 
						|
normal distribution around 0.0d0.
 | 
						|
 | 
						|
Sufficiently positive MIN or negative MAX will cause the algorithm used to
 | 
						|
take a very long time. If MIN is positive it should be close to zero, and
 | 
						|
similarly if MAX is negative it should be close to zero."
 | 
						|
  (macrolet
 | 
						|
      ((valid (x)
 | 
						|
         `(<= (or min ,x) ,x (or max ,x)) ))
 | 
						|
    (labels
 | 
						|
        ((gauss ()
 | 
						|
           (loop
 | 
						|
                 for x1 = (- (random 2.0d0) 1.0d0)
 | 
						|
                 for x2 = (- (random 2.0d0) 1.0d0)
 | 
						|
                 for w = (+ (expt x1 2) (expt x2 2))
 | 
						|
                 when (< w 1.0d0)
 | 
						|
                 do (let ((v (sqrt (/ (* -2.0d0 (log w)) w))))
 | 
						|
                      (return (values (* x1 v) (* x2 v))))))
 | 
						|
         (guard (x)
 | 
						|
           (unless (valid x)
 | 
						|
             (tagbody
 | 
						|
              :retry
 | 
						|
                (multiple-value-bind (x1 x2) (gauss)
 | 
						|
                  (when (valid x1)
 | 
						|
                    (setf x x1)
 | 
						|
                    (go :done))
 | 
						|
                  (when (valid x2)
 | 
						|
                    (setf x x2)
 | 
						|
                    (go :done))
 | 
						|
                  (go :retry))
 | 
						|
              :done))
 | 
						|
           x))
 | 
						|
      (multiple-value-bind
 | 
						|
            (g1 g2) (gauss)
 | 
						|
        (values (guard g1) (guard g2))))))
 | 
						|
 | 
						|
(declaim (inline iota))
 | 
						|
(defun iota (n &key (start 0) (step 1))
 | 
						|
  "Return a list of n numbers, starting from START (with numeric contagion
 | 
						|
from STEP applied), each consequtive number being the sum of the previous one
 | 
						|
and STEP. START defaults to 0 and STEP to 1.
 | 
						|
 | 
						|
Examples:
 | 
						|
 | 
						|
  (iota 4)                      => (0 1 2 3)
 | 
						|
  (iota 3 :start 1 :step 1.0)   => (1.0 2.0 3.0)
 | 
						|
  (iota 3 :start -1 :step -1/2) => (-1 -3/2 -2)
 | 
						|
"
 | 
						|
  (declare (type (integer 0) n) (number start step))
 | 
						|
  (loop ;; KLUDGE: get numeric contagion right for the first element too
 | 
						|
        for i = (+ (- (+ start step) step)) then (+ i step)
 | 
						|
        repeat n
 | 
						|
        collect i))
 | 
						|
 | 
						|
(declaim (inline map-iota))
 | 
						|
(defun map-iota (function n &key (start 0) (step 1))
 | 
						|
  "Calls FUNCTION with N numbers, starting from START (with numeric contagion
 | 
						|
from STEP applied), each consequtive number being the sum of the previous one
 | 
						|
and STEP. START defaults to 0 and STEP to 1. Returns N.
 | 
						|
 | 
						|
Examples:
 | 
						|
 | 
						|
  (map-iota #'print 3 :start 1 :step 1.0) => 3
 | 
						|
    ;;; 1.0
 | 
						|
    ;;; 2.0
 | 
						|
    ;;; 3.0
 | 
						|
"
 | 
						|
  (declare (type (integer 0) n) (number start step))
 | 
						|
  (loop ;; KLUDGE: get numeric contagion right for the first element too
 | 
						|
        for i = (+ start (- step step)) then (+ i step)
 | 
						|
        repeat n
 | 
						|
        do (funcall function i))
 | 
						|
  n)
 | 
						|
 | 
						|
(declaim (inline lerp))
 | 
						|
(defun lerp (v a b)
 | 
						|
  "Returns the result of linear interpolation between A and B, using the
 | 
						|
interpolation coefficient V."
 | 
						|
  ;; The correct version is numerically stable, at the expense of an
 | 
						|
  ;; extra multiply. See (lerp 0.1 4 25) with (+ a (* v (- b a))). The
 | 
						|
  ;; unstable version can often be converted to a fast instruction on
 | 
						|
  ;; a lot of machines, though this is machine/implementation
 | 
						|
  ;; specific. As alexandria is more about correct code, than
 | 
						|
  ;; efficiency, and we're only talking about a single extra multiply,
 | 
						|
  ;; many would prefer the stable version
 | 
						|
  (+ (* (- 1.0 v) a) (* v b)))
 | 
						|
 | 
						|
(declaim (inline mean))
 | 
						|
(defun mean (sample)
 | 
						|
  "Returns the mean of SAMPLE. SAMPLE must be a sequence of numbers."
 | 
						|
  (/ (reduce #'+ sample) (length sample)))
 | 
						|
 | 
						|
(defun median (sample)
 | 
						|
  "Returns median of SAMPLE. SAMPLE must be a sequence of real numbers."
 | 
						|
  ;; Implements and uses the quick-select algorithm to find the median
 | 
						|
  ;; https://en.wikipedia.org/wiki/Quickselect
 | 
						|
 | 
						|
  (labels ((randint-in-range (start-int end-int)
 | 
						|
             "Returns a random integer in the specified range, inclusive"
 | 
						|
             (+ start-int (random (1+ (- end-int start-int)))))
 | 
						|
           (partition (vec start-i end-i)
 | 
						|
             "Implements the partition function, which performs a partial
 | 
						|
              sort of vec around the (randomly) chosen pivot.
 | 
						|
              Returns the index where the pivot element would be located
 | 
						|
              in a correctly-sorted array"
 | 
						|
             (if (= start-i end-i)
 | 
						|
                 start-i
 | 
						|
                 (let ((pivot-i (randint-in-range start-i end-i)))
 | 
						|
                   (rotatef (aref vec start-i) (aref vec pivot-i))
 | 
						|
                   (let ((swap-i end-i))
 | 
						|
                     (loop for i from swap-i downto (1+ start-i) do
 | 
						|
                       (when (>= (aref vec i) (aref vec start-i))
 | 
						|
                         (rotatef (aref vec i) (aref vec swap-i))
 | 
						|
                         (decf swap-i)))
 | 
						|
                     (rotatef (aref vec swap-i) (aref vec start-i))
 | 
						|
                     swap-i)))))
 | 
						|
 | 
						|
    (let* ((vector (copy-sequence 'vector sample))
 | 
						|
           (len (length vector))
 | 
						|
           (mid-i (ash len -1))
 | 
						|
           (i 0)
 | 
						|
           (j (1- len)))
 | 
						|
 | 
						|
      (loop for correct-pos = (partition vector i j)
 | 
						|
            while (/= correct-pos mid-i) do
 | 
						|
              (if (< correct-pos mid-i)
 | 
						|
                  (setf i (1+ correct-pos))
 | 
						|
                  (setf j (1- correct-pos))))
 | 
						|
 | 
						|
      (if (oddp len)
 | 
						|
          (aref vector mid-i)
 | 
						|
          (* 1/2
 | 
						|
             (+ (aref vector mid-i)
 | 
						|
                (reduce #'max (make-array
 | 
						|
                               mid-i
 | 
						|
                               :displaced-to vector))))))))
 | 
						|
 | 
						|
(declaim (inline variance))
 | 
						|
(defun variance (sample &key (biased t))
 | 
						|
  "Variance of SAMPLE. Returns the biased variance if BIASED is true (the default),
 | 
						|
and the unbiased estimator of variance if BIASED is false. SAMPLE must be a
 | 
						|
sequence of numbers."
 | 
						|
  (let ((mean (mean sample)))
 | 
						|
    (/ (reduce (lambda (a b)
 | 
						|
                 (+ a (expt (- b mean) 2)))
 | 
						|
               sample
 | 
						|
               :initial-value 0)
 | 
						|
       (- (length sample) (if biased 0 1)))))
 | 
						|
 | 
						|
(declaim (inline standard-deviation))
 | 
						|
(defun standard-deviation (sample &key (biased t))
 | 
						|
  "Standard deviation of SAMPLE. Returns the biased standard deviation if
 | 
						|
BIASED is true (the default), and the square root of the unbiased estimator
 | 
						|
for variance if BIASED is false (which is not the same as the unbiased
 | 
						|
estimator for standard deviation). SAMPLE must be a sequence of numbers."
 | 
						|
  (sqrt (variance sample :biased biased)))
 | 
						|
 | 
						|
(define-modify-macro maxf (&rest numbers) max
 | 
						|
  "Modify-macro for MAX. Sets place designated by the first argument to the
 | 
						|
maximum of its original value and NUMBERS.")
 | 
						|
 | 
						|
(define-modify-macro minf (&rest numbers) min
 | 
						|
  "Modify-macro for MIN. Sets place designated by the first argument to the
 | 
						|
minimum of its original value and NUMBERS.")
 | 
						|
 | 
						|
;;;; Factorial
 | 
						|
 | 
						|
;;; KLUDGE: This is really dependant on the numbers in question: for
 | 
						|
;;; small numbers this is larger, and vice versa. Ideally instead of a
 | 
						|
;;; constant we would have RANGE-FAST-TO-MULTIPLY-DIRECTLY-P.
 | 
						|
(defconstant +factorial-bisection-range-limit+ 8)
 | 
						|
 | 
						|
;;; KLUDGE: This is really platform dependant: ideally we would use
 | 
						|
;;; (load-time-value (find-good-direct-multiplication-limit)) instead.
 | 
						|
(defconstant +factorial-direct-multiplication-limit+ 13)
 | 
						|
 | 
						|
(defun %multiply-range (i j)
 | 
						|
  ;; We use a a bit of cleverness here:
 | 
						|
  ;;
 | 
						|
  ;; 1. For large factorials we bisect in order to avoid expensive bignum
 | 
						|
  ;;    multiplications: 1 x 2 x 3 x ... runs into bignums pretty soon,
 | 
						|
  ;;    and once it does that all further multiplications will be with bignums.
 | 
						|
  ;;
 | 
						|
  ;;    By instead doing the multiplication in a tree like
 | 
						|
  ;;       ((1 x 2) x (3 x 4)) x ((5 x 6) x (7 x 8))
 | 
						|
  ;;    we manage to get less bignums.
 | 
						|
  ;;
 | 
						|
  ;; 2. Division isn't exactly free either, however, so we don't bisect
 | 
						|
  ;;    all the way down, but multiply ranges of integers close to each
 | 
						|
  ;;    other directly.
 | 
						|
  ;;
 | 
						|
  ;; For even better results it should be possible to use prime
 | 
						|
  ;; factorization magic, but Nikodemus ran out of steam.
 | 
						|
  ;;
 | 
						|
  ;; KLUDGE: We support factorials of bignums, but it seems quite
 | 
						|
  ;; unlikely anyone would ever be able to use them on a modern lisp,
 | 
						|
  ;; since the resulting numbers are unlikely to fit in memory... but
 | 
						|
  ;; it would be extremely unelegant to define FACTORIAL only on
 | 
						|
  ;; fixnums, _and_ on lisps with 16 bit fixnums this can actually be
 | 
						|
  ;; needed.
 | 
						|
  (labels ((bisect (j k)
 | 
						|
             (declare (type (integer 1 #.most-positive-fixnum) j k))
 | 
						|
             (if (< (- k j) +factorial-bisection-range-limit+)
 | 
						|
                 (multiply-range j k)
 | 
						|
                 (let ((middle (+ j (truncate (- k j) 2))))
 | 
						|
                   (* (bisect j middle)
 | 
						|
                      (bisect (+ middle 1) k)))))
 | 
						|
           (bisect-big (j k)
 | 
						|
             (declare (type (integer 1) j k))
 | 
						|
             (if (= j k)
 | 
						|
                 j
 | 
						|
                 (let ((middle (+ j (truncate (- k j) 2))))
 | 
						|
                   (* (if (<= middle most-positive-fixnum)
 | 
						|
                          (bisect j middle)
 | 
						|
                          (bisect-big j middle))
 | 
						|
                      (bisect-big (+ middle 1) k)))))
 | 
						|
           (multiply-range (j k)
 | 
						|
             (declare (type (integer 1 #.most-positive-fixnum) j k))
 | 
						|
             (do ((f k (* f m))
 | 
						|
                  (m (1- k) (1- m)))
 | 
						|
                 ((< m j) f)
 | 
						|
               (declare (type (integer 0 (#.most-positive-fixnum)) m)
 | 
						|
                        (type unsigned-byte f)))))
 | 
						|
    (if (and (typep i 'fixnum) (typep j 'fixnum))
 | 
						|
        (bisect i j)
 | 
						|
        (bisect-big i j))))
 | 
						|
 | 
						|
(declaim (inline factorial))
 | 
						|
(defun %factorial (n)
 | 
						|
  (if (< n 2)
 | 
						|
      1
 | 
						|
      (%multiply-range 1 n)))
 | 
						|
 | 
						|
(defun factorial (n)
 | 
						|
  "Factorial of non-negative integer N."
 | 
						|
  (check-type n (integer 0))
 | 
						|
  (%factorial n))
 | 
						|
 | 
						|
;;;; Combinatorics
 | 
						|
 | 
						|
(defun binomial-coefficient (n k)
 | 
						|
  "Binomial coefficient of N and K, also expressed as N choose K. This is the
 | 
						|
number of K element combinations given N choises. N must be equal to or
 | 
						|
greater then K."
 | 
						|
  (check-type n (integer 0))
 | 
						|
  (check-type k (integer 0))
 | 
						|
  (assert (>= n k))
 | 
						|
  (if (or (zerop k) (= n k))
 | 
						|
      1
 | 
						|
      (let ((n-k (- n k)))
 | 
						|
        ;; Swaps K and N-K if K < N-K because the algorithm
 | 
						|
        ;; below is faster for bigger K and smaller N-K
 | 
						|
        (when (< k n-k)
 | 
						|
          (rotatef k n-k))
 | 
						|
        (if (= 1 n-k)
 | 
						|
            n
 | 
						|
            ;; General case, avoid computing the 1x...xK twice:
 | 
						|
            ;;
 | 
						|
            ;;    N!           1x...xN          (K+1)x...xN
 | 
						|
            ;; --------  =  ---------------- =  ------------, N>1
 | 
						|
            ;; K!(N-K)!     1x...xK x (N-K)!       (N-K)!
 | 
						|
            (/ (%multiply-range (+ k 1) n)
 | 
						|
               (%factorial n-k))))))
 | 
						|
 | 
						|
(defun subfactorial (n)
 | 
						|
  "Subfactorial of the non-negative integer N."
 | 
						|
  (check-type n (integer 0))
 | 
						|
  (if (zerop n)
 | 
						|
      1
 | 
						|
      (do ((x 1 (1+ x))
 | 
						|
           (a 0 (* x (+ a b)))
 | 
						|
           (b 1 a))
 | 
						|
          ((= n x) a))))
 | 
						|
 | 
						|
(defun count-permutations (n &optional (k n))
 | 
						|
  "Number of K element permutations for a sequence of N objects.
 | 
						|
K defaults to N"
 | 
						|
  (check-type n (integer 0))
 | 
						|
  (check-type k (integer 0))
 | 
						|
  (assert (>= n k))
 | 
						|
  (%multiply-range (1+ (- n k)) n))
 |