diff --git a/numbers.lisp b/numbers.lisp index bf6ef0bf2ee46e6276b8777fc64805fbbbf73afb..0fa29a43b06d63868eb630fb0c460e73e4586c7c 100644 --- a/numbers.lisp +++ b/numbers.lisp @@ -210,6 +210,10 @@ greater then 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: @@ -231,8 +235,8 @@ greater then K." (defun count-permutations (n &optional (k n)) "Number of K element permutations for a sequence of N objects. -R defaults to N" - ;; FIXME: Use %multiply-range and take care of 1 and 2, plus - ;; check types. - (/ (factorial n) - (factorial (- n k)))) +K defaults to N" + (check-type n (integer 0)) + (check-type k (integer 0)) + (assert (>= n k)) + (%multiply-range (1+ (- n k)) n)) diff --git a/tests.lisp b/tests.lisp index c13956850ba38b0543c793423c9f42bcc510af16..b9e2277bc2d0b9a6b2bd8575c3ca61c506c90226 100644 --- a/tests.lisp +++ b/tests.lisp @@ -1712,3 +1712,21 @@ (1 2 3) nil nil) + +(deftest count-permutations.1 + (values (count-permutations 31 7) + (count-permutations 1 1) + (count-permutations 2 1) + (count-permutations 2 2) + (count-permutations 3 2) + (count-permutations 3 1)) + 13253058000 + 1 + 2 + 2 + 6 + 3) + +(deftest binomial-coefficient.1 + (alexandria:binomial-coefficient 1239 139) + 28794902202288970200771694600561826718847179309929858835480006683522184441358211423695124921058123706380656375919763349913245306834194782172712255592710204598527867804110129489943080460154)