Last active
September 17, 2024 13:38
-
-
Save purcell/34824f1b676e6188540cdf71c7cc9fc4 to your computer and use it in GitHub Desktop.
Various methods of randomly shuffling a list or vector of elements in emacs lisp, with benchmarks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defun key-quiz--shuffle-list (list) | |
"Shuffles LIST randomly, modying it in-place." | |
(dolist (i (reverse (number-sequence 1 (1- (length list))))) | |
(let ((j (random (1+ i))) | |
(tmp (elt list i))) | |
(setf (elt list i) (elt list j)) | |
(setf (elt list j) tmp))) | |
list) | |
(defun key-quiz--shuffle-list-nreverse (list) | |
"Shuffles LIST randomly, modying it in-place." | |
(dolist (i (nreverse (number-sequence 1 (1- (length list))))) | |
(let ((j (random (1+ i))) | |
(tmp (elt list i))) | |
(setf (elt list i) (elt list j)) | |
(setf (elt list j) tmp))) | |
list) | |
(defun elfeed--shuffle (seq) | |
"Destructively shuffle SEQ." | |
(let ((n (length seq))) | |
(prog1 seq ; don't use dotimes result (bug#16206) | |
(dotimes (i n) | |
(cl-rotatef (elt seq i) (elt seq (+ i (random (- n i))))))))) | |
(defun faster-seq-sort-by (function pred sequence) | |
"Sort SEQUENCE using PRED as a comparison function. | |
Elements of SEQUENCE are transformed by FUNCTION before being | |
sorted. FUNCTION must be a function of one argument." | |
;; This version is modified to avoid calling "random" twice every time the predicate is called. | |
(seq-map 'cdr | |
(sort (seq-map (lambda (x) (cons (funcall function x) x)) sequence) | |
(lambda (a b) | |
(funcall pred (car a) (car b)))))) | |
(defun seq-sort-by--shuffle (seq) | |
(seq-sort-by (lambda (_) (random)) #'<= seq)) | |
(defun faster-seq-sort-by--shuffle (seq) | |
(faster-seq-sort-by (lambda (_) (random)) #'<= seq)) | |
(defun faster-seq-sort-by--shuffle (seq) | |
(faster-seq-sort-by (lambda (_) (random)) #'<= seq)) | |
(defun my-seq-shuffle (sequence) | |
"Unrolled version of seq-sort-by" | |
(seq-map 'cdr | |
(sort (seq-map (lambda (x) (cons (random) x)) sequence) | |
(lambda (a b) | |
(<= (car a) (car b)))))) | |
(defun hugot-shuffle (list) | |
"Method from hugot, tweaked to avoid converting vecs back into lists when not needed" | |
(let* ((vec (seq-into list 'vector)) | |
(length (length vec)) | |
(n 0)) | |
(while (< n length) | |
(let ((i (+ n (random (- length n)))) | |
(tmp (aref vec n))) | |
(setf (aref vec n) (aref vec i) | |
(aref vec i) tmp)) | |
(cl-incf n)) | |
(if (vectorp list) | |
vec | |
(seq-into vec 'list)))) | |
(defun elfeed-on-vec-shuffle (list) | |
"Elfeed's method, but converting to vec" | |
(let* ((seq (seq-into list 'vector))) | |
(let ((n (length seq))) | |
(prog1 (if (vectorp list) seq (seq-into seq 'list)) | |
(dotimes (i n) | |
(cl-rotatef (elt seq i) (elt seq (+ i (random (- n i)))))))))) | |
(dolist (sorter '(key-quiz--shuffle-list | |
key-quiz--shuffle-list-nreverse | |
elfeed--shuffle | |
seq-sort-by--shuffle | |
faster-seq-sort-by--shuffle | |
my-seq-shuffle | |
hugot-shuffle | |
elfeed-on-vec-shuffle | |
)) | |
(message "\n*** %s ***" sorter) | |
(dolist (list-type '(list vector)) | |
(let ((big-list (seq-into (number-sequence 1 5000) list-type)) | |
(reps 100) | |
;;(gc-cons-threshold (* 128 1024 1024)) | |
) | |
(message "-- %s:" list-type) | |
(garbage-collect) | |
(benchmark reps `(funcall ',sorter big-list)) | |
))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*** key-quiz--shuffle-list *** | |
-- list: | |
Elapsed time: 6.650204s | |
-- vector: | |
Elapsed time: 0.240027s | |
*** key-quiz--shuffle-list-nreverse *** | |
-- list: | |
Elapsed time: 6.742569s | |
-- vector: | |
Elapsed time: 0.244173s | |
*** elfeed--shuffle *** | |
-- list: | |
Elapsed time: 11.157770s | |
-- vector: | |
Elapsed time: 0.236123s | |
*** seq-sort-by--shuffle *** | |
-- list: | |
Elapsed time: 0.644674s | |
-- vector: | |
Elapsed time: 0.685449s | |
*** faster-seq-sort-by--shuffle *** | |
-- list: | |
Elapsed time: 0.573194s | |
-- vector: | |
Elapsed time: 0.572976s | |
*** my-seq-shuffle *** | |
-- list: | |
Elapsed time: 0.470165s | |
-- vector: | |
Elapsed time: 0.465306s | |
*** hugot-shuffle *** | |
-- list: | |
Elapsed time: 0.209031s | |
-- vector: | |
Elapsed time: 0.197855s | |
*** elfeed-on-vec-shuffle *** | |
-- list: | |
Elapsed time: 0.252475s | |
-- vector: | |
Elapsed time: 0.239680s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@purcell great, thanks!