Some clarifications. On Wed, Feb 16, 2011 at 8:50 PM, Ken Wesson <kwess...@gmail.com> wrote: > Perfect squares are also worst-case (other than actual primes)
To be exact, perfect squares /of primes/. Squares of composite integers halted on a smaller prime factor even in the original version. > could be a bit more efficient. (It's likely to depend on your data. If > squares are common in the data this may help; if not the (== x y) test > may not be worth it.) Prime-squares would have to be common in the data. My guess would be that the (== x y) test makes it slightly slower on typical data. > This last of course gobbles memory remembering earlier primes. The > "user/prime?" is required in 1.2 for the function to call the memoized > version of itself that is created after. It might be desired to hoist > the var lookup out of the loop Actually, the prime? lookup should only be occurring once when filter's arguments are evaluated anyway. The var lookups that could actually slow things down are the zero? and rem in the closure, which could be getting done repeatedly. So ... (defn prime? [n] (if-not (< n 2) (let [x (inc (int (Math/sqrt n))) zero? clojure.core/zero? rem clojure.core/rem] (not-any? #(zero? (rem n %)) (filter user/prime? (range 2 x)))))) (def prime? (memoize prime?)) seems best for frequent calls with smallish values of n; for larger values or less frequent use the non-heap-consuming (defn prime? [n] (if-not (< n 2) (let [x (inc (int (Math/sqrt n))) zero? clojure.core/zero? rem clojure.core/rem] (not-any? #(zero? (rem n %)) (cons 2 (take-nth 2 (range 3 x))))))) will be preferable and for even larger values (defn prime? [n] (if-not (< n 2) (if (probable-prime? n) (let [x (inc (int (Math/sqrt n))) zero? clojure.core/zero? rem clojure.core/rem] (not-any? #(zero? (rem n %)) (filter probable-prime? (range 2 x))))))) coupled with a suitable function definition for probable-prime? (it should be quite fast but kill off a large swath of composite numbers). Or you can parametrize it and make it the caller's choice, e.g. (defn prime? ([n] (or (== 2 n) (prime? odd? n))) ([probable-prime? n] (if-not (< n 2) (if (probable-prime? n) (let [x (inc (int (Math/sqrt n))) zero? clojure.core/zero? rem clojure.core/rem] (not-any? #(zero? (rem n %)) (filter probable-prime? (range 2 x)))))))) (The one-argument case works despite odd? not being a true probable-prime test (it returns false for prime number 2) because a) it explicitly checks for n = 2 and returns true in that case and b) although divisibility by this prime is also not tested in the inner loop, it is precisely divisibility by this prime that (if (probable-prime? n) ...) ends up testing. Thus the one-argument case ends up working basically the same as the first non-memoized implementation in this post, checking against 2, 3, 5, 7, 9, 11, 13, 15 ...) Another class of speedup is to generalize (cons 2 (take-nth 2 (range ...))) in the second implementation in a different way: consing the primes below some number k onto the numbers prime to k. Example with k = 24: (def p-b-24 [2 3 5 7 11 13 17 19 23]) (def p-t-24 [1 5 7 11 13 17 19 23]) (defn primes-superset [] (concat p-b-24 (mapcat #(map (partial + (* 24 %)) p-t-24) (iterate inc 1)))) user=> (take 50 (primes-superset)) (2 3 5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59 61 65 67 71 73 77 79 83 85 89 91 95 97 101 103 107 109 113 115 119 121 125 127 131 133 137 139 143 145) The case k = 2 amounts to (cons 2 (take-nth 2 (range 3 ...))) Of course, (primes-superset) returns an infinite seq. The algorithm changes a bit more to still stop at the square root of n: (defn prime? [n] (if-not (< n 2) (let [x (inc (int (Math/sqrt n))) zero? clojure.core/zero? rem clojure.core/rem] (not-any? #(zero? (rem n %)) (take-while #(< % x) (primes-superset)))))) Note that for primes-superset that returns just (iterate inc 2) the take-while expression amounts to (range 2 x). This can be combined with probable-prime tests or memoization, too: use (take-while #(< % x) (primes-superset)) in place of (range 2 x) in the first or third implementation in this post to reduce memory spent on memoizing some of the composite numbers' compositeness or time spent in probable-prime? in cases where probable-prime? is relatively complex and slow. Then you might want to use (def p-b-24 [2 3 5 7 11 13 17 19 23]) (def p-b-24s (set p-b-24)) (def p-t-24 [1 5 7 11 13 17 19 23]) (defn primes-superset [] (concat p-b-24 (mapcat #(map (partial + (* 24 %)) p-t-24) (iterate inc 1)))) (defn prime? [n] (or (p-b-24s n) (prime?* n))) (defn prime?* [n] (... one of earlier implementations with (range 2 x) goes here with (range 2 x) replaced by (take-while #(< % x) (primes-superset)))) (def prime?* (memoize prime?*)) ; if necessary. Then numbers not either in the primes up to k or relatively prime to k get wiped quickly, numbers higher than k that are relatively prime to k get memoized or get a more expensive pseudo-primality test performed on them, and numbers that survive the pseudo-primality test as well get the full treatment. The primes up to k also don't get memoized. -- 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 Note that posts from new members are moderated - please be patient with your first post. 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