Problem 39 - ピタゴラス数
オイラーといえば、フェルマーの最終定理に挑んだ数学者のひとり。
フェルマーの最終定理といえば、a^n + b^n = c^n。
というわけで、Problem 39 - PukiWiki
辺の長さが{a,b,c}と整数の3つ組である直角三角形を考え, その周囲の長さをpとする. p = 120のときには3つの解が存在する:
{20,48,52}, {24,45,51}, {30,40,50}
p < 1000 で解の数が最大になる p を求めよ.
何処の数を軸に考えるかというのが問題になりそう。
pを軸に考えると爆発的に計算量が増えてしまうので、a,bを軸にして、a <= bで回してみる。
すると、pをキャッシュしておく必要が出てくるので破壊的にベクタを使ってみる。
(define (problem39 n) (letrec ((square (lambda (n) (* n n))) (data (make-vector n 0)) (inc! (lambda (n) (vector-set! data n (+ 1 (vector-ref data n))))) (iter (lambda (a b) (let* ((c (sqrt (+ (square a) (square b)))) (p (+ a b c))) (cond ((>= a n) data) ((>= p n) (iter (+ a 1) (+ a 1))) (else (if (exact? c) (inc! p)) (iter a (+ b 1)))))))) (vector-fold (lambda (i acc v) (if (> v (cdr acc)) (cons i v) acc)) '(0 . 0) (iter 1 1)))) (problem39 121) ; (120 . 3) (problem39 1000) ; (840 . 8)
いっこづつ増やすのは無駄そうな気がするんだけど、√(a^2 + b^2)が整数になる条件がイマイチわからない。
ピタゴラス数の生成式
a = m^2 - n^2, b = 2mn, c = m^2 + n^2(m>nでm,nは互いに素)を使うと、a,b,cそれぞれが互いに素なピタゴラス数を生成できるが、
今回の場合互いに素ではないピタゴラス数も求めなければならないので、倍数を取る必要がある。
しかぁし・・・重複が出るので、重複を取り除く手段が必要になる・・・・。
できた
やってることはたいしたことないんだけど、alistのユーティリティをいくつか追加したので案外長め。
(define (collect l) (letrec ((inc-alist (lambda (x a) (cond ((null? a) (list (cons x 1))) ((eq? (caar a) x) (cons (cons x (+ 1 (cdar a))) (cdr a))) (else (cons (car a) (inc-alist x (cdr a)))))))) (fold-right inc-alist '() l))) (collect '(1 1 1 2 2 3)) ; ((3 . 1) (2 . 2) (1 . 3)) (define (max-alist l) (fold (lambda (x m) (if (or (not m) (> (cdr x) (cdr m))) x m)) #f l)) (max-alist (collect '(1 1 1 2 2 3))) ; (1 . 3) (define (problem39 x) (letrec ((square (lambda (n) (* n n))) (calc (lambda (sum l n) (if (>= (* n sum) x) '() (cons (map (lambda (x) (* n x)) l) (calc sum l (+ n 1)))))) (iter (lambda (m n acc) (let* ((a (- (square m) (square n))) (b (* 2 m n)) (c (+ (square m) (square n))) (sum (+ a b c))) (cond ((>= sum x) (if (= n m) acc (iter (+ n 1) (+ n 1) acc))) ((not (= (gcd m n) 1)) (iter (+ m 1) n acc)) (else (iter (+ m 1) n (lset-union equal? acc (calc sum (list (min a b) (max a b) c) 1))))))))) (max-alist (sort (collect (map (lambda (l) (apply + l)) (iter 2 1 '()))) (lambda (a b) (< (car a) (car b))))))) (problem39 121) ; (120 . 3) (problem39 1000) ; (840 . 8)
メインのループは16^2以下しか回らない。かなり早くなった。後は集計を早くすると・・・。
参考
- さんすう・数学
- 単位円から生成式を求める。ちと複雑だけど正統派。
- ピタゴラス数の求め方
- 偶数、奇数から生成式を求める。シンプル。
- ピタゴラス数の発見
- Σ(2k-1)からの方法。ちょっと違う式になるみたい。