Miller-Rabin pseudo-primality test (was: Debugging support for clojure?)

2009-03-12 Thread Tassilo Horn

Mark Engelberg mark.engelb...@gmail.com writes:

Hi Mark,

 For number theory you often need things like

    (mod (expt n exp) m)

 Yes, and to make things like this fast, the trick is to perform the
 mod at each multiplication step, rather than waiting until the end.

So now I added this suggestion and the test is now really, really fast.
I tried some really big primes from the list of prime numbers on
wikipedia, and it always finishes instantly.  And what's best: the
results seem to be correct, too. ;-)

Here's the code.  Please feel free to comment on where I could do better
or use a more ideomatic style.

--8---cut here---start-8---
(ns de.tsdh.clojure.math.primes
  ;; TODO: Only expt from math is needed, but how do I use :only with multiple
  ;; libs?
  (:use [clojure.contrib math test-is]))

(def
 #^{:doc The chances that prime? returns true for a composite if *pseudo*
is true is (expt 4 (* -1 *pseudo-accuracy*)).}
 *pseudo-accuracy* 10)

(defn factorize-out
  Factorizes out all x factors from n.
Examples:
  (factorize-out 10 2) == 5, because 2^1 * 5 = 10
  (factorize-out 90 3) == 10, because 3^2 * 10 = 90
  [n x]
  (loop [acc n exp 0]
(if (= 0 (mod acc x))
  (recur (/ acc x) (inc exp))
  (hash-map :exponent exp :rest acc

(defn expt-mod
  Equivalent to (mod (expt n e) m), but faster.
http://en.wikipedia.org/wiki/Modular_exponentiation#An_efficient_method:_the_right-to-left_binary_algorithm;
  [n e m]
  (loop [r 1, b n, e e]
(if (= e 0)
  r
  (recur (if (odd? e)
   (mod (* r b) m)
   r)
 (mod (expt b 2) m)
 (bit-shift-right e 1)

(defn prime?
  Checks if n is a prime using the Miller-Rabin pseudo-primality test.  Also
see *pseudo* and *pseudo-accuracy*.
  [n]
  (cond
( n 2)   false
(= n 2)   true
(even? n) false
:else (let [m (factorize-out (dec n) 2)
d (:rest m)
s (:exponent m)]
(loop [k 1]
  (if ( k *pseudo-accuracy*)
true
(let [a (+ 2 (rand-int (- n 4)))
  x (expt-mod a d n)]
  (if (or (= x 1) (= x (dec n)))
(recur (inc k))
(if (loop [r 1
   x (expt-mod x 2 n)]
  (cond
   (or (= x 1) (  r (dec s)))  false
   (= x (dec n))true
   :else (recur (inc r) (mod (* x x) n
  (recur (inc k))
  false

(defn next-prime [n]
  Returns the next prime greater than n.
  (cond
( n 2)2
:else (loop [n (inc n)]
(if (prime? n)
  n
  (recur (inc n))

;; test stuff

(def
 #^{:private true}
 first-1000-primes
 '(   2  3  5  7 11 13 17 19 23 29 
 ;; [Snipped, but the test works with all 1000 primes...]
   7841   7853   7867   7873   7877   7879   7883   7901   7907   7919))

(deftest test-prime-fns
  (loop [a (take 1000 (iterate next-prime 2)) 
 b (take 1000 first-1000-primes)]
(when (and (empty? a) (empty? b))
  (is (= (first a) (first b)))
  (recur  (rest a) (rest b)
--8---cut here---end---8---

Bye,
Tassilo

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Clojure group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---



Re: Miller-Rabin pseudo-primality test (was: Debugging support for clojure?)

2009-03-12 Thread Jerry K

On Mar 12, 2:45 pm, Tassilo Horn tass...@member.fsf.org wrote:
 Mark Engelberg mark.engelb...@gmail.com writes:

 Hi Mark,

  For number theory you often need things like

     (mod (expt n exp) m)

  Yes, and to make things like this fast, the trick is to perform the
  mod at each multiplication step, rather than waiting until the end.

 So now I added this suggestion and the test is now really, really fast.
 I tried some really big primes from the list of prime numbers on
 wikipedia, and it always finishes instantly.  And what's best: the
 results seem to be correct, too. ;-)

Glad your problem is resolved.  Miller-Rabin is quite zippy...  and
practical for real use.  If you want a next cool exercise to take a
crack at, the direction one often goes is to improve the speed of the
underlying multiplications using the FFT...


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Clojure group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~--~~~~--~~--~--~---