素因数分解もういっかい

  • 素因数分解: ツムジのひとりごと
    • ありがとうございます。111111111の計算に48秒もかかるのは私のミスです。factorizeの終了条件に[< n (expt p 2)]が抜けていたのが原因です。お見苦しいコードをお見せしました。
(use util.stream)
(define (composites s)
  (stream-delay
   (let1 p (stream-car s)
     (stream-cons (expt p 2)
                  (xor (stream-map (pa$ * p) (xor (stream-cdr s) (composites s)))
                       (composites (stream-cdr s)))))))

(define (xor s0 s1)
  (stream-delay
   (let ((x (stream-car s0))
         (y (stream-car s1)))
     (cond
      ((= x y) (xor (stream-cdr s0) (stream-cdr s1)))
      ((< x y) (stream-cons x (xor (stream-cdr s0) s1)))
      ((> x y) (stream-cons y (xor s0 (stream-cdr s1))))))))

(define primes (stream-cons 2 (xor (stream-iota -1 3) (composites primes))))

(define (divisor n m count)
  (if (zero? (modulo n m))
    (divisor (quotient n m) m (+ 1 count))
    (values n count)))

(define (factorizer n strm)
  (let loop ([n n] [strm strm] [l '()])
    (let ((p  (stream-car strm))
          (ps (stream-cdr strm)))
      (cond 
       ([= n 1]          (reverse l))
       ([< n (expt p 2)] (reverse (cons (cons n 1) l)))
       (else
        (receive (r count) (divisor n p 0)
          (if (zero? count)
            (loop r ps l)
            (loop r ps (cons (cons p count) l)))))))))

(define (main args)
  (print (factorizer (string->number (cadr args))
                     primes)))
% time gosh factStrm.scm 111111111
((3 . 2) (37 . 1) (333667 . 1))
gosh factStrm.scm 111111111  0.10s user 0.01s system 96% cpu 0.111 total

以上です。

素数ストリームを使う時点で速さは諦めていますが、きれいに書けるので好きです。
ツムジさんのコードは速くてすばらしいです。

  • 自分用メモ:ツムジさんのコードを俺流に書いたもの。記録のため残す。
(define (divisor n m count)
  (if (zero? (modulo n m))
    (divisor (quotient n m) m (+ 1 count))
    (values n count)))

(define (factorize n)
  (let loop ([n n] [i 2] [l '()])
    (receive (r count) (divisor n i 0)
      (cond
       ([= n 1]          (reverse l))
       ([> (expt i 2) n] (reverse (cons (cons n 1) l)))
       (else             (loop r
                               (+ i (if (= i 2) 1 2))
                               (if (zero? count)
                                 l
                                 (cons (cons i count) l))))))))

(define (main args)
  (print (factorize (string->number (cadr args)))))

(追記 2009/5/31)
factorizerはstrmを受け取る汎用性があるので、ツムジさんのように素数列を求めずに"2と奇数列"でやるときは、こうします。

(define (main args)
  (print (factorizer (string->number (cadr args))
                     (stream-cons 2 (stream-iota -1 3 2)))))