4.4 while文

(1/1)
エッシャーの階段
エッシャーの階段
Python で繰り返し処理を行うには、前回学んだ for文のほかに、while文がある。while文は条件式によって繰り返すかどうかを制御するときに用い、複利計算はもちろん、約数やニュートン法(近似計算法)によって平方根を求めることができる。

コラムでは、while文で注意しなければならない無限ループや、解の方程式が存在しないときにwhile文を使うケースなどについて紹介する。

目次

サンプル・プログラム

複利計算

4.3 for文」で作った複利計算プログラム "calcCompoundInterest2.py" だが、期間 period が短いと、境界値を超える前に for文が終了してしまい、期待通りの結果を得られない。
そこで、今回は境界値にスポットを当て、while文を使ってプログラムを改良してみることにする。
def calcCompoundInterest3(deposit, interest, target):
	"""複利計算(目標金額になったら計算終了)
	
	Args:
		deposit(int):		現在の預金額
		interest(float):	年間利率
		period(int):		期間(年)
		target(int):		目標金額
	
	Returns:
while文
while文は次のようにして使う。
while 条件式:
    処理1
    処理2
    ...
条件式が成立する間、インデントになっているブロックを実行する。
プログラム "calcCompoundInterest3.py" では、預金額 deposit が目標値 target 未満である間、複利計算を続ける。また、何年目の計算をしているのか分かるように、変数 year に計算回数を代入する。
while文を使うことで、if文break を使うこともなく、簡単に結果を求めることができるようになる。

約数を求める

次に、約数を求めるプログラムを作ってみよう。
def calcFactor(num):
	"""約数を求める.

	エラトステネスのふるいを用いる.

	Args:
		num(int): 整数
	
	Returns:
整数 \(N, a, b \) について \( N = a \times b \) が成り立つとき \( a \) は \( N \) の約数という。ここで、 \( a \neq 0 \) とする。言い方を変えると、 \( N \) を割り切れる自然数 \( a \) を \( N \) の約数という。1と \( N \) 自身も \( N \) の約数である。

この定義をPythonプログラムにしたものが "calcFactor.py" である。
整数 \( N \) の約数は、 \( N \) 自身を除くと、最大でも \( N \) の2分の1整数である。無駄なループ回数を省くため、ループの終了条件は \( N \) の2分の1整数とする。
自然数 \( d \) を1から1ずつ加算していき、剰余演算子を使って割り切れれば、約数としてリスト factor に加えてゆく。リストについては、「5.3 リストとin演算子」で詳しく説明する。

約数を求める方程式は見つかっていない。そこで、"calcFactor.py" ではループを回して、1つずつ約数に該当するかどうかを力ずくで計算する。このように方程式が存在しない問題の解を求めるとき、while文がよく使われる。

ニュートン法

次に、ニュートン法を使って、平方根の近似値を求めるプログラム "newtonsSqrt1.py" を紹介しよう。
def newtonsSqrt1(y, e):
	"""
	ニュートン法を用いて平方根を求める
	@param	float y		平方根を求めたい数
	@param	float e		許容誤差
	@return	float 平方根
	"""
	x = y * 2		# xの初期値
	while (True):
		# ニュートン法で新しいyを求める
		x2 = x - (x * x - y) / (x * y)
		# 誤差範囲内になったら計算終了
		if (math.fabs(x2 - x) < e):
			break;
		x = x2
	return x;
ニュートン法とは、 \( f(x)=0 \) の解 \( x \) を求める近似計算法である。
ある適当な値 \( x_0 \) から計算を開始し、
\[ x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} \]
という漸化式を計算すると、\( n \) が大きくなるほど解 \( x \) に収束していく。

平方根 \( \sqrt y \) の解を求めるには、
\[ f(x) = x^2 - y \]
とおき、 \( f(x)=0 \) の解を求めることを考える。
ここで、 \( f'(x)=2x \) であることから、漸化式は
\[ x_{n+1} = x_n - \frac{x_{n}^2 - 2}{2 x_n} = \frac{1}{2}(x_n + \frac{2}{x_n}) \]
となる。
あらかじめ誤差 \( e \) を設けておき、\( x_{n}^2 \) と \( y \) の差が \( e \)より小さくなったら break文を使って漸化式を終了する(while文から抜け出す)。これをプログラムにしたものが "newtonsSqrt1.py" である。

練習問題

次回予告

プログラムを作っていると、さまざまなエラー対策をしなければならない。たとえば「2.3 入出力とエラー対策」で取り上げた入力エラー対策が1つの例だ。
プログラムの中で対策できるエラーであれば、if文などを使って制御できるが、組み込み関数や、文法そのもののエラーの場合もある。
次回は、Python に備わっている例外処理機能を使って、こうしたエラーを処理する方法を学ぶ。

コラム:無限ループ

無限に続く螺旋階段
while文 の不具合として一番気をつけなければならないのが、条件式が常に成立してしまい、無限に繰り返し処理を行うことだ。これを無限ループと呼ぶ。
本文中の "newtonsSqrt1.py" は、条件式が while (True) で常に成立するので、ブロック中の break文が正しく働かなければ無限ループに陥ってしまう。本文でも触れたが、本来は再帰呼び出しで書くべきである。
一方の for文は、range関数を使って確実に繰返し回数を制約できるので、無限ループに陥るリスクは低い。Python回数が決まった繰り返し処理をしたければ、for文を使うことをお勧めする

ニュートン法のような漸化式を扱う場合――級数展開や数列を扱う場合には while文を使うことになるのだが‥‥じつは、数学には繰り返し制御という概念がない。そのための式が存在しない。漸化式や級数の定義は、繰り返しではなく再帰呼び出しで実装するのが本来あるべき姿だろう。再帰呼び出しについては、「5.5 再帰呼び出し」で詳しく取り上げる。

コラム:天体の位置計算

プログラム電卓「FX-502P」
プログラム電卓「FX-502P」
コラム:プログラム電卓から人工知能へで紹介したカシオのプログラム電卓「FX-502P」だが、私は1986年(昭和61年)に地球に接近するハレー彗星の位置計算をしたくて、プログラミングをはじめた。300年以上前に、イギリスの天文学者エドモンド・ハレーが行ったように‥‥。

だが、『三体問題 天才たちを悩ませた400年の未解決問題』(浅田秀樹=著,講談社,2021年3月)にあるように、天体の位置を解く方程式は存在しない。このため漸化式を使って近似計算をしていくのだが――。
エドモンド・ハレー
エドモンド・ハレー
エドモンド・ハレーは、万有引力の法則でお馴染みのアイザック・ニュートンの友人だった。本文に登場するニュートン法は、アイザック・ニュートンの名前に由来するわけだが、彼は微積分法を確立し、計算機が存在しなかった時代に、複雑なニュートン方程式の近似解を求める手段も提供したのである。ある意味、数学が苦手だったアインシュタインに比べ、ニュートンの天才ぶりは世界史上でも傑出していたと言えよう。そのせいかどうか分からないが、自分の肖像画を大量に描かせたり、錬金術にのめり込んだりと、ニュートンの奇行ぶりは世界史上でも特筆すべきものであった――天才とはそういうものなのだろう。
さて、ニュートン方程式の近似解を求める漸化式は、本文で紹介したような簡単なものではない。当時のプログラム電卓の小さなメモリには、ループ部分を収めるのが精一杯で、それ以外の計算は別のプログラムに分離して、プログラムをロードし直さなければならなかった。
それでも、関数電卓を使って漸化式の結果を計算帳に手書きでメモした時に比べれば格段に早く計算ができた。

なぜプログラムを書くのかとたずねられたら、私の答えは40年前も今も変わらない――問題解決の手間を省きたいから――。
節約した時間を遊びに使ってもいいし、あらたな問題解決に使ってもいい――皆さんもプログラムという道具を使って、自分の自由になる時間を増やしてほしい。

コラム:エラトステネスの篩

本文で約数を求めるプログラム "calcFactor.py" を紹介したが、これは指定した自然数 \( N \) 以下のすべての素数を見つけ出すアルゴリズム「エラトステネスの篩」の応用である。
本文でも述べたように、小学校の算数で学ぶ約数を求める方程式は未発見である。解法が存在しない以上、前述の漸化式と同じで力任せの計算に頼るしかなく、コンピュータが活躍する場となっている。かつて素因数分解に悩まされた方がいるとしたら、それはあなたのせいではない。あれは本来、コンピュータにやらせるべき課題なのである。

ただ、コンピュータパワーも無限ではない。数千桁、数万桁の素数をエラトステネスの篩で求めようとすると、さすがに時間がかかる。
そこで、数学者たちは視点を変えて、与えられた自然数 \( N \) が素数であるかどうかを高い確率で判定する方法を編み出した。その1つがミラー・ラビン素数判定法である。
(この項おわり)
header