平方根の計算、再び

30分プログラム、その652。平方根の求め方の方法で、平方根の計算方法を試してみました。
「なんで奇数を引くんだよ。わけ解んねーよ」と騒いでたら、id:Roccoさん、別名@hi_saitoさんに、平方根の求め方というサイトを紹介してもらいました。

というわけで、さっそくコードに落してみよう。
でも、なかなかややこしくて、まだ整数の範囲しか扱えません。でも、前回の平方根の計算 - みずぴー日記よりは高速だと思います。

使い方

gosh> (sqrt 529)
(2 3)

ソースコード

#! /opt/local/bin/gosh
;; -*- mode:scheme; coding:utf-8 -*-
;;
;; sqrt.scm -
;;
;; Copyright(C) 2009 by mzp
;; Author: MIZUNO Hiroki / mzpppp at gmail dot com
;; http://howdyworld.org
;;
;; Timestamp: 2009/09/02 21:21:30
;;
;; This program is free software; you can redistribute it and/or
;; modify it under MIT Lincence.
;;
(use gauche.collection)

(define (prev odd)
  (- odd 2))

(define (next odd)
  (+ odd 2))

(define (sqrt-digit n i)
  (let loop [(n n)
	     (i i)
	     (count 0)
	     ]
    (if (< n i)
	(values count n (prev i))
	(loop (- n i) (next i)  (+ 1 count)))))

(define (split-digit n unit)
  (let loop [(n n)
	     (xs '())]
    (if (< n unit)
	(cons n xs)
	(receive (q r)
		 (quotient&remainder n unit)
		 (loop q (cons r xs))))))

(define (sqrt n)
  (receive (n _)
	   (map-accum (lambda (n accum)
			(receive (ans r minus)
				 (sqrt-digit (+ n (* 100 (car accum)))
					     (cdr accum))
				 (values ans
					 (cons r (+ (* (+ minus 1) 10) 1)))))
		      (cons 0 1)
		      (split-digit n 100))
	   n))