Gaucheで素数ストリーム
エラトステネスの篩
(define (divisor? x y) (eqv? 0 (remainder x y))) (define-constant +primes+ (stream-cons 2 (stream-filter prime? (stream-iota -1 3 2)))) ;; (define (prime? n) ;; (not (stream-any (cut divisor? n <>) (stream-take-while (cute >= (sqrt n) <>) +primes+)))) (define (prime? n) (let1 limit (sqrt n) (let loop ((srm +primes+)) (let1 x (stream-car srm) (or (< limit x) (if (divisor? n x) #f (loop (stream-cdr srm)))))))) ;(time (stream-ref |+primes+| 10000)) ; real 0.868 ; user 0.840 ; sys 0.020 ; => 104743
stream-take-while を使った版は 5.132s かかった.
Gaucheでエラトステネスの篩で素数ストリーム(世間一般のやつより速い) - Gemmaの日記 のと比較してみたら,向こうのは 7.792s かかったのでこちらの方が大分速い.
でもこれを Haskell に移植して Haskell 版で比較すると 0.179s と 0.123s で向こうの方が速かった.
ところが,-O つけてコンパイルするとどちらも 0.104s くらいで同じになった…
takeWhile を使うと 0.695s と遅くなっていたのが -O 付きでは 0.078s と一番速くなった.
HaskellのtakeWhile展開版
primes = 2 : filter isPrime [3,5..] {- isPrime n = not $ any (isDivisor n) $ takeWhile (limit >=) primes where limit = floor $ sqrt $ fromIntegral n -} isPrime n = loop primes where loop (x:xs) | limit < x = True | isDivisor n x = False | otherwise = loop xs where limit = floor $ sqrt $ fromIntegral n isDivisor n m = rem n m == 0 main = print $ primes !! 10000