ラグランジュの四平方定理

演習問題の、ラグランジュの四平方定理(2003年 ICPC アジア地区予選 会津大会 問題B)

http://osmag.jp/articles/root/root93.html

(define (lag n)
  (define count 0)
  (define (cmp l)
    (let1 s (apply + (map (lambda (x) (* x x)) l))
      (cond
       ((= s n) 0)
       ((> s n) 1)
       (else -1))))
  
  (let loop0 ((a (x->integer (sqrt n))))
    (unless (< a 0)
      (unless (= 1 (cmp (list a)))
        (let loop1 ((b a))
          (unless (< b 0)
            (unless (= 1 (cmp (list a b)))
              (let loop2 ((c b))
                (unless (< c 0)
                  (unless (= 1 (cmp (list a b c)))
                    (let loop3 ((d c))
                      (unless (< d 0)
                        (when (= 0 (cmp (list a b c d)))
                          (inc! count))
                        (loop3 (- d 1)))))
                  (loop2 (- c 1)))))
            (loop1 (- b 1)))))
      (loop0 (- a 1))))
  count)

(define (main args)
  (print (lag (x->integer (cadr args)))))
time gosh lag.scm 20007
738

real    0m15.154s
user    0m15.108s
sys     0m0.010s
  • こんな入れ子のループを使うんじゃ、Cで書いたのと変わらない。
  • amb かなにかで書き直したほうがいいかなぁ。速度が犠牲になりそうだけど。
  • Haskell はすごいよ id:zyxwv:20070531
  • Ruby で amb 使ってる。すごい。 id:mzp:20070531
  • (追記) Cで書いたらどうなるよ。
#include <stdio.h>
#include <math.h>

int cmp (int a, int b, int c, int d, int n)
{
  int s = a * a + b * b + c * c + d * d;
  if (n == s) return 0;
  if (n < s) return 1;
  return -1;
}

int main (int argc, char **argv)
{
  int count = 0;
  int i,j,k,l;
  int n = 20007;
  for (i = (int) sqrt (n); i >= 0; i--) {
    if (cmp (i,0,0,0,n) == 1) continue;
    for (j = i; j >= 0; j--) {
      if (cmp (i,j,0,0,n) == 1) continue;
      for (k = j; k >= 0; k--) {
	if (cmp (i,j,k,0,n) == 1) continue;
	for (l = k; l >= 0; l--) {
	  if (cmp (i,j,k,l,n) == 0) count++;
	}
      }
    }
  }
  printf("%d\n", count);
  return 0;
}
time ./a.out
738

real    0m0.116s
user    0m0.114s
sys     0m0.002s

ぎゃふん。