Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add approximate counting algorithm in Emacs Lisp #882

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions book.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
"lang": "elm",
"name": "Elm"
},
{
"lang": "elisp",
"name": "Emacs Lisp"
},
{
"lang": "LabVIEW",
"name": "LabVIEW"
Expand Down
2 changes: 2 additions & 0 deletions contents/approximate_counting/approximate_counting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions contents/approximate_counting/code/elisp/approximate-counting.el
Original file line number Diff line number Diff line change
@@ -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)