前の関連記事:Python(8)線形再帰を非再帰に変換する方法
前回再帰関数の動きを図にする方法がわかっただけでだいぶ理解が進みました。今度はいろいろ再帰関数の例を非再帰に変換してみます。
ユークリッドの互除法
ユークリッドの互除法とは自然数xとyの最大公約数を求める方法です。
gcd(x,y)={gcd(y,xmod
yが0になるまで関数gcd()を繰り返し実行していけば、最大公約数が求められます。
ということでこれは漸化式なので再帰で表現できます。
1 2 3 4 5 6 |
def gcd(x, y): if y = = 0 : return x else : return gcd(y, x % y) print (gcd( 24 , 9 )) |
これは自分自身を呼び出したあとに何もしていませんので、末尾再帰になります。
1 2 3 4 5 6 7 8 |
def gcd(x, y): if y = = 0 : return x else : x % = y # xをyで割った余りをxに代入。 x, y = y, x # xとyの中身を入れ換え。 return gcd(x, y) print (gcd( 24 , 9 )) |
return gcd(x, y)は何を返している?
7行目のgcd(x, y)をreturnしていますが、これは何を返しているのでしょう?
計算結果の「3」は3行目のreturn xで返されているはずです。
1 2 3 4 5 6 7 8 |
def gcd(x, y): if y = = 0 : return x else : x % = y x, y = y, x gcd(x, y) print (gcd( 24 , 9 )) |
戻り値がないということです。
これは図に書いてみると理解できました。
このとき帰り道で、つまりgcd(x, y)を呼び出した後で、returnを実行していることになるので末尾再帰と言っていいのだろうか、と疑問に思ったのですが、どうもそれでよいようです。
末尾再帰の型にはめて再帰の除去をする
1 2 3 4 5 |
def foo(n): CODE_A if CONDITION_A: CODE_B foo(m) |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def gcd(x, y): if y = = 0 : # CODE_A return x if y > 0 : # CONDTION_A x % = y # CODE_B x, y = y, x return gcd(x, y) print (gcd( 24 , 9 )) |
1 2 3 4 5 |
def foo(n): CODE A while CONDITION A: CODE_B CODE_A |
1 2 3 4 5 6 7 8 9 |
def gcd(x, y): if y = = 0 : return x while y > 0 : x % = y x, y = y, x if y = = 0 : return x print (gcd( 24 , 9 )) |
関数gcd()の引数x, yは自然数のみと考えると2行目のif文がTrueになることはないので省略できます。
1 2 3 4 5 6 7 |
def gcd(x, y): while y > 0 : x % = y x, y = y, x if y = = 0 : return x print (gcd( 24 , 9 )) |
1 2 3 4 5 6 |
def gcd(x, y): while y > 0 : x % = y x, y = y, x return x print (gcd( 24 , 9 )) |
while文を書いているとwhileの前とwhileの中に同じコードがでてきてなんとかならないかな、と思ったことがあるのですが、それは再帰に変形できる場合で、このCODE_Aに相当する部分だったのかも。
0 件のコメント:
コメントを投稿