10001번째의 소수?
에라토스테네스 체(Sieve of Eratosthenes)를 이용하면 소수 목록을 구할 수 있지만, 이 문제의 경우 체의 범위를 어디까지 해야 할지가 애매하다. 대충 50,000 정도까지 해보고 안 되면 다시 100,000 정도까지 해보는 식으로 범위를 늘려가며 문제를 풀 수도 있다.
방법 1
어떤 수가 소수인지 빠르게 판단할 수 있다면 숫자를 증가시켜가며 소수만 걸러내는 방법을 사용하는 것도 가능하다. 소수인지 판별하는 함수는 다음과 같이 작성할 수 있다.
(defn prime?
"Returns true if n is prime."
[n]
(cond (< n 2) false
(< n 4) true ; 2, 3
(even? n) false
(< n 9) true ; 5, 7
(= 0 (mod n 3)) false
:else (nil? (take 1 (filter #(= 0 (mod n %))
(range 3 (inc (int (Math/sqrt n))) 2))))))
짝수(=2의 배수) 또는 3의 배수는 소수가 아니므로 바로 false
를 리턴한다. 다른 경우에는 까지 숫자를 증가시키며 나눠보는 로직이 포함되어 있다. 중간에 하나라도 나눠지는 경우가 있으면 바로 false
를 리턴할 것이다.
소수인지 판단하는 prime?
함수를 구현했다면 10,001번째 소수는 다음과 같이 구할 수 있다.
(defn using-predicate []
(->> (iterate inc 2)
(filter prime?)
(drop (dec limit))
first))
방법 2
clojure.contrib.lazy-seqs/primes
를 이용하는 간단한 방법도 있다. 이름이 암시하듯 모든 소수에 대한 지연 시퀀스를 나타낸다. 따라서 10,001번째 소수는 다음과 같이 구할 수도 있다.
(require '[clojure.contrib.lazy-seqs :refer [primes]])
(defn using-seq []
(->> primes
(drop (dec limit))
first))
정리
실행해보면 두 방법 모두 충분히 빠르게 답을 구한다.
p007=> (time (using-predicate)) "Elapsed time: 239.324261 msecs" 104??? p007=> (time (using-seq)) "Elapsed time: 107.451821 msecs" 104???