Pythonプログラミング(ステップ4・反復処理・連分数)

連分数の計算について考える。

1.連分数と実数の「よい」近似法

実数 2=1.41421356 をなるべく「良く」近似できるような分数の表式(有理数)を見つけたい。 いちばん荒い近似としては 1 が考えられるが、これでは大雑把すぎてあまり実用的とは言えない。 141100=1.41 くらいにすれば、かなり近い値と言えるかもしれないが、 たとえば 75=1.4 とした場合でも、それほど値に大きな違いは無い。 その意味では、75141100に比べて「無駄なく」2を近似していると言えるだろう。

このように「無駄」が少なく良い近似を与えるような有理数を求める方法として、連分数展開(continued fraction expansion)が知られている。

2を例に、実際にそれを行ってみよう。

調べたい数を (整数部) + (小数部) に分解する。2の場合は 2=1+(21)=1+0.41421356である。 ここで、 a0=1b0=10.41421356=121=1+2 と置くと 2=a0+1/b0 と分解できたことになる。小数部の逆数 b0 を考えるところがミソである。

次に、b0に対しても、同様に整数部と小数部に分け b0=1+2=2+0.41421356 とすることで、 b0=a1+1/b1 とさらに分解できる。 ただし、ここで a1=2b1=10.41421356=121=1+2 である。

同様の操作を繰り返すと、展開のn段目(ただしn1)では an=2bn=1+2 であることが確認できる。

以上をまとめると、2の連分数展開として 2=1+12+12+12+1 が得られたことになる。

連分数を数式で表現するのはなかなか厄介であるため、上記の連分数は anを並べて [1;2,2,2,2,2,] のように表記することが多い。 同様に 3を連分数展開すると [1;1,2,1,2,1,2,] であり、黄金比のそれは [1;1,1,1,1,1,1,] となる。

2は無理数であることから、この展開は無限に続くが、それをn段目で打ち切った数を cn とすれば、 c0=1 c1=1+12=32=1.5 c2=1+12+12=75=1.4 c3=1+12+12+12=1712=1.41666 といった具合に、「小さい」分数で良い近似値が得られていることがわかる。

icon-pc 練習:連分数展開

以下に、実数値を入力すると、その連分数展開(anの値)を出力するPythonコードの例を示す。

具体的な値を入力して動作を確認してみなさい。

リストや関数について知識のある者は、各段階での近似値(cn)を出力するように改良してみなさい。

# coding: utf-8

import math

x = float(input("x="))
b = x
while True:
    a = math.floor(b)
    print(a,"+ 1/")    
    if abs(b-a) < 0.0001:
        break
    b = 1/(b-a)
icon-hint ヒント

math.floor()は、実数値の整数部分(小数部を切り捨てた値)を与える関数である。 反対に、切り上げはmath.ceil()

icon-teacher 解説: fractionsモジュール

連分数の計算などを行う際に、分数は分数として計算したくなる。そのような用途に、Pythonの標準モジュールの中にfractionsが含まれている。

詳しい機能はマニュアル等を参照してもらうこととして、連分数展開を与えると、その値を有理数で表示するコードの例の以下に示す。

# coding: utf-8

from fractions import Fraction

contfrac = [1,2,2,2,2,2]

c = Fraction(contfrac.pop())

for a in reversed(contfrac):
    c = Fraction(a) + Fraction(1)/Fraction(c)

print(c)  

上記のコードには、これから学ぶ事項(リスト)が含まれているが、計算の流れは理解できるはずだ。

contfrac.pop() はリストの最後の要素を取り出す(同時に削除する)操作、 for a in reversed(contfrac):は、リストの最後から順に要素を取り出してcにセットする操作を表す。