From 868bbb4ed36c92e82850058d369e25e98ed84e4e Mon Sep 17 00:00:00 2001 From: Eric Berquist Date: Sun, 17 Oct 2021 12:35:20 -0400 Subject: [PATCH] Add approximate counting algorithm in Emacs Lisp --- .editorconfig | 5 +++ book.json | 4 ++ .../approximate_counting.md | 2 + .../code/elisp/approximate-counting.el | 43 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 contents/approximate_counting/code/elisp/approximate-counting.el diff --git a/.editorconfig b/.editorconfig index a3fbd6e02..92015f567 100644 --- a/.editorconfig +++ b/.editorconfig @@ -51,6 +51,11 @@ indent_size = 4 indent_style = space indent_size = 2 +# Emacs Lisp +[*.el] +indent_style = space +indent_size = 2 + # Emojicode [*.emojic] indent_style = space diff --git a/book.json b/book.json index 690acec87..b61231fd7 100644 --- a/book.json +++ b/book.json @@ -102,6 +102,10 @@ "lang": "elm", "name": "Elm" }, + { + "lang": "elisp", + "name": "Emacs Lisp" + }, { "lang": "LabVIEW", "name": "LabVIEW" diff --git a/contents/approximate_counting/approximate_counting.md b/contents/approximate_counting/approximate_counting.md index 654721844..5598028a0 100644 --- a/contents/approximate_counting/approximate_counting.md +++ b/contents/approximate_counting/approximate_counting.md @@ -366,6 +366,8 @@ As we do not have any objects to count, we will instead simulate the counting wi [import, lang:"cpp"](code/c++/approximate_counting.cpp) {% sample lang="python" %} [import, lang:"python"](code/python/approximate_counting.py) +{% sample lang="elisp" %} +[import, lang:"elisp"](code/elisp/approximate-counting.el) {% endmethod %} ### Bibliography diff --git a/contents/approximate_counting/code/elisp/approximate-counting.el b/contents/approximate_counting/code/elisp/approximate-counting.el new file mode 100644 index 000000000..afb69a3c5 --- /dev/null +++ b/contents/approximate_counting/code/elisp/approximate-counting.el @@ -0,0 +1,43 @@ +(require 'cl-extra) + +(defun sum (things) + "Return the sum of all THINGS." + (apply '+ things)) + +(defun n (v a) + "Return the approximate count at a given register value V. +A is a scaling value for the logarithm based on Morris' paper." + (* a (- (expt (+ 1 (/ 1 (float a))) v) 1))) + +(defun increment (v a) + "Return V, randomly incremented by one or as-is. +A is a scaling value for the logarithm based on Morris' paper." + (let ((delta (/ 1 (- (n (1+ v) a) (n v a))))) + (if (<= (cl-random 1.0) delta) + (1+ v) + v))) + +(defun approximate-count (nitems a) + "Simulate the counting of NITEMS, returning an approximate count. +A is a scaling value for the logarithm based on Morris' paper." + (let ((v 0)) + (dotimes (i nitems) + (setq v (increment v a))) + (n v a))) + +(defun test-approximate-count (ntrials nitems a threshold) + "Test approximate counting, printing when passing. +NITEMS items are counted for NTRIALS trials to within a THRESHOLD error +(float between 0 and 1). +A is a scaling value for the logarithm based on Morris' paper." + (let* ((samples (mapcar (lambda (x) (approximate-count nitems a)) (number-sequence 1 ntrials))) + (avg (/ (sum samples) ntrials))) + (if (< (abs (/ (- avg nitems) nitems)) threshold) + (princ "passed\n")))) + +(princ "testing 1,000, a = 30, 1% error\n") +(test-approximate-count 100 1000 30 0.1) +(princ "testing 12,345, a = 10, 1% error\n") +(test-approximate-count 100 12345 10 0.1) +(princ "testing 222,222, a = 0.5, 10% error\n") +(test-approximate-count 100 222222 0.5 0.2)