src/number-letters.lisp @ d2c07bcc6144
Add number chains
author |
Steve Losh <steve@stevelosh.com> |
date |
Thu, 15 Sep 2016 14:23:13 +0000 |
parents |
(none) |
children |
833316fc5296 |
(in-package #:sand.number-letters)
; https://www.youtube.com/watch?v=LYKn0yUTIU4
(declaim (optimize (debug 0) (safety 1) (speed 3)))
;;;; Slow/Reference Implementation --------------------------------------------
(defun number-string (n)
(format nil "~R" n))
(defun slow-letter-count (n)
(count-if #'alpha-char-p (number-string n)))
;;;; Fast Version -------------------------------------------------------------
(define-constant +small-counts+
(make-array 1000
:element-type 'fixnum
:initial-contents (iterate (for i :from 0 :below 1000)
(collect (slow-letter-count i)))))
(defparameter *suffixes*
(cons ""
(iterate (for i :from 1 :to 21)
(collect (subseq (format nil "~R" (expt 1000 i)) 4)))))
(define-constant +suffix-lengths+ (mapcar #'length *suffixes*)
:test #'equal)
(declaim (ftype (function ((integer 0)) fixnum)
fast-letter-count))
(defun fast-letter-count (n)
(declare (optimize (debug 0) (safety 0) (speed 3)))
(if (zerop n)
4
(iterate
(for i :first n :then (floor i 1000))
(for sl :in +suffix-lengths+)
(while (not (zerop i)))
(for part = (mod i 1000))
(when (not (zerop part))
(sum sl)
(sum (aref +small-counts+ part))))))
(defun sanity-check ()
(iterate (for i :from 1 :to 10000000)
(finding i :such-that (not (= (fast-letter-count i)
(slow-letter-count i))))))
;;;; Chains -------------------------------------------------------------------
(defun chain-length (n)
(if (= n 4)
1
(1+ (chain-length (fast-letter-count n)))))
(defun print-chain (n)
(let ((lc (letter-count n)))
(format t "~D - ~R -> ~D~%" n n lc)
(when (not (= n 4))
(print-chain lc))))
(define-constant +cache-size+ 1000)
(define-constant +cache+
(make-array +cache-size+
:element-type 'fixnum
:initial-contents (iterate (for i :from 0 :below +cache-size+)
(collect (chain-length i)))))
(defun chain-length% (n)
(iterate
(for i :first n :then (fast-letter-count i))
(summing 1 :into result)
(declare (type fixnum result))
(when (< i +cache-size+)
(return (the fixnum (+ result (aref +cache+ i)))))))
(defun longest-chain (max)
(iterate
(for i :from 1 :to max)
(finding i :maximizing (chain-length% i))))
; (time
; (print-chain (longest-chain (expt 10 9))))