30分プログラム 回文積(Project Euler)
from みずぴー日記 Project Euler Problem 4
左右どちらから読んでも同じ値になる数を回文数という。 2桁の数の積で表される回文数のうち、最大のものは 9009 = 91 * 99 である。
では、3桁の数の積で表される回文数のうち最大のものはいくらになるか。
Haskellの遅延評価砲が火を吹きそうな問題だけど、Gaucheで。
id:mzpがambでやったので、じゃあappend-mapでやるべと思ってこんなコードを書いたけど・・・。
(use srfi-1) (define (test) (fold (lambda (a b) (if (> (car a) (car b)) a b)) '(0 0 0) (append-map (lambda (x) (append-map (lambda (y) (let* ((z (* x y)) (zl (map digit->integer (string->list (x->string z)))) (zr (reverse zl))) (if (every = zl zr) (list (list z x y)) '()))) (iota 900 100))) (iota 900 100))))
=>(906609 913 993)
11秒@ThinkpadX60
ガウディ本にも回文積が載っていた。曰く、「制約プログラミングを使えば、6桁の回文積を0.2秒でリストアップできるぜい、500MHzのCPUで」だそうです。
じゃあ高速にしてやんよ
副作用使ってやる。あと、123456->"123456"->("1" "2" "3" "4" "5" "6")->(1 2 3 4 5 6)の処理は文字列操作を挟んで遅いので、ちゃんとquotientとmoduloを使うまっとうなものを。z=x*y で 100 * 300 も 300 * 100 も同じだから省ける。
(use srfi-1) (define (integer->list n) (cons (modulo n 10) (if (< n 10) '() (integer->list (quotient n 10))))) (define (test2) (define result '(0 0 0)) (define digit (iota 900 100)) (for-each (lambda (x) (for-each (lambda (y) (and-let* ((z (* x y)) ((< (car result) z)) (zl (integer->list z)) (zr (reverse zl)) ((every = zl zr))) (set! result (list z x y)))) (iota (- x 99) 100))) digit) result)
=>(906609 913 993)
0.6秒になった。
(use srfi-1) (define (integer->list n) (cons (modulo n 10) (if (< n 10) '() (integer->list (quotient n 10))))) (define (test3) (define result '(0 0 0)) (let loop ((x 100) (y 100)) (cond ((> y 999) (loop (+ x 1) 100)) ((> x 999) result) ((< x y) (loop (+ x 1) 100)) ((>= x y) (and-let* ((z (* x y)) ((< (car result) z)) (zl (integer->list z)) (zr (reverse zl)) ((every = zl zr))) (set! result (list z x y))) (loop x (+ y 1))))))
0.5秒になった。
はじめはリストだけでサクッとつくっておいて、あとでデータ構造なり副作用なりループに開くなりで高速化する、これがLisp脳だぜっ