Prime factorization
Every natural number can be expressed as a product of primes. For instance, 10 = 2 x 5 and 24 = 2 x 2 x 2 x 3.
Your task is to write a function that takes a number n as an argument and returns a list of its prime factors.
Prime factorization
Every natural number can be expressed as a product of primes. For instance, 10 = 2 x 5 and 24 = 2 x 2 x 2 x 3.
Your task is to write a function that takes a number n as an argument and returns a list of its prime factors.
| (defn factor | |
| ([n] | |
| (factor 2 [] n)) | |
| ([p ps n] | |
| (cond | |
| (= 1 n) | |
| ps | |
| (zero? (rem n p)) | |
| (recur p (conj ps p) (quot n p)) | |
| :else | |
| (recur (inc p) ps n)))) |
| ;; My first cut - this seemed to give correct answers, but | |
| ;; when I gave it the 50millionth prime to factor, it took about 40 seconds. | |
| (defn factors [n] | |
| (let [multiple-of? (fn [n div] (zero? (mod n div))) | |
| divisors (lazy-seq (cons 2 (iterate (partial + 2) 3)))] | |
| (loop [res [] | |
| n n | |
| divs divisors] | |
| (let [d (first divs)] | |
| (if (> d n) | |
| res | |
| (if (multiple-of? n d) | |
| (recur (conj res d) | |
| (quot n d) | |
| divs) | |
| (recur res | |
| n | |
| (rest divs)))))))) | |
| ;; First attempt to speed it up - I figured replacing the lazy sequence of divisors | |
| ;; with a "next-div" fn would be faster. It was - now did 50millionth prime in 12 secs. | |
| (defn factors [n] | |
| (let [multiple-of? (fn [n div] (zero? (mod n div))) | |
| next-div (fn [d] | |
| (if (= 2 d) 3 (+ d 2)))] | |
| (loop [res [] | |
| n n | |
| d 2] | |
| (if (> d n) | |
| res | |
| (if (multiple-of? n d) | |
| (recur (conj res d) | |
| (quot n d) | |
| d) | |
| (recur res | |
| n | |
| (next-div d))))))) | |
| ;; Next attempt to speed up further. I figured the 'next-div' logic was simple once | |
| ;; we got to odd divisors, so I pulled-out the "odd-ball case" of dividing by 2 and this | |
| ;; was an additional win. Factors 50millionth prime in 10 seconds. | |
| (defn remove-twos [n] | |
| (loop [n n | |
| twos []] | |
| (if (odd? n) | |
| [n twos] | |
| (recur (quot n 2) | |
| (conj twos 2))))) | |
| (defn factors [n] | |
| (let [multiple-of? (fn [n div] (zero? (mod n div))) | |
| [odd-n twos] (remove-twos n)] | |
| (loop [res twos | |
| n odd-n | |
| d 3] | |
| (if (> d n) | |
| res | |
| (if (multiple-of? n d) | |
| (recur (conj res d) | |
| (quot n d) | |
| d) | |
| (recur res | |
| n | |
| (+ d 2))))))) |
| (defn primes-up-to | |
| "Given a positive integer N, returns a vector of the prime numbers up to n." | |
| [N] | |
| ;; Implementation using an Eratosthenes Sieve | |
| (if (< N 2) | |
| [] | |
| (let [N (int N) | |
| sieve (boolean-array (inc N) false)] | |
| (loop [n (int 2)] | |
| (cond | |
| (aget sieve n) | |
| ;; not a prime, next | |
| (recur (unchecked-inc-int n)) | |
| (< N (* n n)) | |
| ;; we're done, package the result | |
| (into [] | |
| (remove #(aget sieve %)) | |
| (range 2 (inc N))) | |
| :else | |
| ;; n is a prime, marking its multiples | |
| (do | |
| (loop [m (unchecked-add-int n n)] | |
| (when (<= m N) | |
| (aset sieve m true) | |
| (recur (unchecked-add-int m n)))) | |
| (recur (unchecked-inc-int n)))))))) | |
| (defn divides? [d n] | |
| (-> n (rem d) (= 0))) | |
| (defn reduce-by-prime | |
| "Trims out p in the prime factorization of m, i.e | |
| returns the highest divisor of n not divisible by p." | |
| [p n] | |
| (if (divides? p n) | |
| (recur p (/ n p)) | |
| n)) | |
| (comment | |
| (reduce-by-prime 5 100) => 4 | |
| (reduce-by-prime 7 100) => 100) | |
| (defn prime-factors | |
| [n] | |
| (if (< n 2) | |
| (sorted-set) | |
| (into (sorted-set) | |
| (let [s (int (Math/ceil (Math/sqrt n))) | |
| potential-prime-divisors (primes-up-to s)] | |
| (loop [m n | |
| ppds potential-prime-divisors | |
| prime-divisors []] | |
| (if (or (= m 1) (empty? ppds)) | |
| (if (empty? prime-divisors) | |
| #{n} | |
| prime-divisors) | |
| (let [pd (first ppds) | |
| m1 (reduce-by-prime pd m)] | |
| (recur | |
| m1 | |
| (rest ppds) | |
| (cond-> prime-divisors | |
| (< m1 m) | |
| (conj pd)))))))))) | |
| (comment | |
| (prime-factors 31) | |
| (prime-factors 120) | |
| (prime-factors 4) | |
| (prime-factors 2) | |
| (let [ ;; let's try this on a big random number | |
| n (* | |
| (rand-int Integer/MAX_VALUE) | |
| (rand-int 1000000))] | |
| (time ;; takes about 1s on my machine, most of it is spent computing the sieve. | |
| [n (prime-factors n)]))) |
| (ns prime-factors) | |
| (require 'clojure.math.numeric-tower) | |
| ; start at 2, then run through every odd number | |
| (defn of-1 [n] | |
| (loop [m n | |
| div 2 | |
| acc []] | |
| (cond | |
| (zero? (mod m div)) (recur (/ m div) | |
| div | |
| (conj acc div)) | |
| (< m 2) acc | |
| :else (recur m (if (odd? div) | |
| (+ 2 div) | |
| (inc div)) acc)))) | |
| ; start at 2, then run through the odd numbers that can divide the | |
| ; given number evenly | |
| (defn of-2 [n] | |
| (loop [running-n n | |
| divisor 2 | |
| factors []] | |
| (cond | |
| (zero? (mod running-n divisor)) (recur (/ running-n divisor) | |
| divisor | |
| (conj factors divisor)) | |
| (< running-n 2) factors | |
| :else (recur running-n | |
| (if (odd? divisor) | |
| (loop [running-divisor divisor] | |
| (cond | |
| (zero? (rem running-n running-divisor)) | |
| running-divisor | |
| :else | |
| (+ 2 running-divisor))) | |
| (inc divisor)) | |
| factors)))) | |
| ; run through 2, 3, and every 6n-1, 6n+1 that divides n evenly | |
| (defn of [n] | |
| (loop [running-n n | |
| divisors (conj (filter (fn [x] (or (= 1 (rem x 6)) | |
| (= 5 (rem x 6)))) | |
| (range 5 (inc n))) | |
| 3 | |
| 2) | |
| factors []] | |
| (cond | |
| (zero? (rem running-n (first divisors))) | |
| (recur (/ running-n (first divisors)) | |
| divisors | |
| (conj factors (first divisors))) | |
| (< running-n 2) | |
| factors | |
| :else | |
| (recur running-n | |
| (drop-while (fn [x] (pos? (rem running-n x))) | |
| divisors) | |
| factors)))) |