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 -~----------~----~----~----~------~----~------~--~---