平方根

会社のプログラム見ていたら、平方根を求める関数があったので、
Pythonで書いてみました。

def sqrt(var):
	if var <= 0:
		return 0
	
	Xn_1 = var      # 漸化式の和
	Xn   = Xn_1 + 1 # 漸化式の和の1つ前
	while Xn_1 < Xn:
		Xn = Xn_1
		Xn_1 = (Xn + (var / Xn)) / 2.0 # ニュートン法
	return Xn

ただ、携帯では実数演算はつらいので、実際のプログラムは、

Xn_1 = (Xn + (var / Xn)) >> 1

となっていましたがー。
 
 
kenmoは最近、関数プログラムに興味があるので、
無理矢理、lambda式を使ってみました。

def sqrt(var):
	if var <= 0:
		return 0
	
	newton = lambda a, x: (x + (a / x)) / 2.0 # ニュートン法の無名関数
	
	Xn_1 = var
	Xn   = Xn_1 + 1
	while Xn_1 < Xn:
		Xn = Xn_1
		Xn_1 = newton(var, Xn)
	return Xn

まあ、マクロを入れただけですね、、。
 
でも、ちょっとだけコードが読みやすくなった気がします。
 
 
ただ、これでは関数プログラムとは言えないなー
なんかいい方法ないかなー。
 
と、ネットを検索していたら、
http://sky.zero.ad.jp/~zaa54437/programming/ruby/part2.htm
これを発見して、
再帰でループを排除する」
「変数を排除する」
ということを知り、再帰に書き換えました。

def sqrt(var, n=9999):
	newton = lambda a, x: (x + (a / x)) / 2.0

	if var <= 0:
		return 0
	elif newton(var, n) >= n:
		return n
	else:
		return sqrt(var, newton(var, n))

ただ、第二引数が9999なのと、newton式を2回演算しているのが、イマイチですね(´Д`;
 
 
だいぶ、プログラムが宣言的になった気がしなくもないですが…、

def newton(a, x):
	return (x + (a / x)) / 2.0
def sqrt(var, n):
	if var <= 0:
		return 0
	elif newton(var, n) >= n:
		return n                         # 基本条件
	else:
		return sqrt(var, newton(var, n)) # 再帰ステップ

 

呼び出し方法

>>>print sqrt(2, 2)
>>>1.41421356237

…うーん、かっこ悪い、、。
まー、とりあえず、これがkenmoの限界です、、。
 
 

追記

reduceを使えば、2行で書けることが判明しました…!
 
reduceっていうのは、
第一引数に関数オブジェクト、
第二引数に引数のリストを受け取る
という関数で、
渡された関数に引数のリストをガシガシ詰め込んで、累積した結果を返すものです。

def sqrt(a, cnt=5):
	return reduce(lambda x, a: (x + (a / x)) / 2.0, [a]*cnt)

第二引数は、演算回数です。
これを増やすほど、誤差が小さくなります。
 
実行結果

>>>print sqrt(5)
>>>2.23606889564

すげー!Pythonすげーよー