1.9. forループ, whileループ¶
繰り返し似たような作業を実行させるときには、forループを使う。書式は微妙に違うがC/C++やJava、その他の言語でも頻繁に使う構文だ。ただNumPyを上手に用いるとforループを使わなくても同じことができる場合が多く、実際のところforループを使わないでNumPyを利用した方が圧倒的に処理速度が速いので、NumPyに慣れてきたらなるべくforループを使わないように頭を働かせることをお勧めする。
書式としては、for i in range(0, 10):
のように、最後にコロン(:
)を書いてforループの中身はインデントを入れる。JupyterLabの場合は、:
を書いて改行すると自動的にインデントが入る。他の言語と違ってpythonでは、このインデントを入れないと文法エラーになる。
(例) 1, 2, …, 10まで足し合わせるといくらになるか計算せよ。
forループを使えば、
total = 0
for i in range(1, 11): #11は含まれない
total += i
print(total)
となる。3行目のtital += i
は、total = total + i
の省略形である。他にもa -= b
、a *= b
などのように略すことができる。それぞれa = a - b
、a = a * b
を略したものである。ループを回したい範囲はrange(start, stop, step)
で指定する。ただし、stop
は含まれないので注意。上の例のようにstep
を省略すると1と解釈される。
forループを使わずに、NumPyを使えば、
import numpy as np
a = np.arange(1, 11)
total = np.sum(a)
print(total)
のようにa
を配列として定義して和np.sum
を取ればいい。むしろこの方が高速に計算できる。
(例) $n=10$のとき、$n!$を求めよ。
練習のためforループを使えば、
n = 10
fact = 1
for i in range(1,n+1):
fact *= i #fact = fact * iの略
print(fact)
一方、NumPyには、数列の積を求めるnp.prod
というfunctionが用意されているので、この場合もNumPyを使う方が計算速度が速くて簡単である。
a = np.arange(1,n+1)
fact = np.prod(a)
print(fact)
また、math moduleには階乗を計算するfunctionがある。
import math
print(math.factorial(n))
forループの書式はrange
を使う以外もたくさんある。例えば、配列a
というものが下のように定義されていたとする
a = np.arange(3, 6)
print(a)
これをさきほどのrange
を使って
for i in range(3):
print(a[i])
としてもいいし、簡単にa
の配列の要素分だけforループを回すという意味で、
for n in a:
print(n)
としてもよい。この場合のn
はa
の要素に対応する。さらに以下のように、in
のあとはlistを置くこともできる。
list_a = ['Beaf', 'Egg', 'Milk']
for n in list_a:
print(n)
配列のように、いくつかの要素をひとまとめにしたもののことをコンテナオブジェクトと呼ぶが、このコンテナオブジェクトに渡ってforループを回すことが多いので、for n in 変数名
のような形式のforループはpythonでよく使われる。実は文字列そのものもコンテナオブジェクトなので、以下のような使い方もできる。
name = "test"
for n in name:
print(n)
コンテナオブジェクトをenumerate
すれば、インデックス番号と要素をそれぞれ取り出すこともできる。enumerateは日本語で「列挙する」という意味。
for n, item in enumerate(list_a):
print(n, item)
数値計算の場合はNumPyを使うので、一般的にはなるべくforループを使わないことをお勧めするが、このfor n in 配列名:
や、for n in enumerate(配列名)
のような書き方は一般的なpythonのコードでよく見かけるので、使いこなせるようになるとプログラミングの幅が広がる。
whileループ¶
forループはループの回数をあらかじめ指定しなければいけないが、回数は決めずにある条件を満たすまではずっとループを回したい、という場合も多い。その場合はwhileループを用いる。while
の後に条件を書けばよい。
i = 0
while i < 100:
i += 1
print(i)
条件の書き方は==
, <=
, などがある。複数の条件がある場合は、and
やor
で結べばよい。0 < i < 3
のような書き方もできる。詳細はif文のところで取り上げる。
i = 3
while 0 < i < 5:
print(i)
i += 1
練習問題¶
(1.9.1.) ネイピア数$e$(自然対数の底)は、以下のように定義されている。 \begin{equation} e = \lim_{n \to +\infty} \left( 1+ \frac{1}{n} \right) ^n \end{equation}
この式から$n=10^8$での値を求め、NumPyでネイピア数を表すnp.e
と比較せよ。
(1.9.2.) 下記の定義で表される数列をフィボナッチ数列という。
\begin{eqnarray} F_0 &=& 0, \\ F_1 &=& 1, \\ F_{n+2} &=& F_n + F_{n+1}, \\ n &\geq& 0 \end{eqnarray}5桁までのフィボナッチ数列を格納する配列を作れ。
解答例¶
(1.9.1.) forループを使うなら、
n = int(1e8)
e = 1
for m in range(1,n+1):
e *= 1 + 1/n #e = e * (1 + 1/n)のこと
print(e)
print(np.e - e)
pythonのforループは遅いので、数秒程度(かそれ以上)計算に時間がかかる。
もっと簡単なのは、単純にべき乗**
を使う方法だろう。
n = int(1e8)
e = (1 + 1/n)**n
print(e)
print(np.e - e)
二つの方法とも大体同じ値になっている。(が微妙に差がある。これはアルゴリズムの違いによる誤差で、誤差の評価は改めて考える。) 大体8桁の精度が出ている。$n=10^8$ぐらいまで大きくすると、おそらくforループの遅さを体感できるに違いない。
(1.9.2.) 桁数は$5$桁までと指定されているが、何回ループを回してよいかは自明でないので、whileループを使う。桁を調べるには、求めたフィボナッチ数の$\log_{10}$(np.log10
)を取って、$5$より小さいときにはwhileを回すようにする。
import numpy as np
F = np.array([0, 1])
i = 1
Fi = np.array([1])
while np.log10(Fi) < 5:
F = np.hstack([F, Fi]) #np.ndarrayの連結
i += 1
a = F[i-1] + F[i]
Fi = np.array([F[i-1] + F[i]])
print(F)
当サイトのテキスト・画像の無断転載・複製を固く禁じます。
Unauthorized copying and replication of the contents of this site, text and images are strictly prohibited.
© 2019 Go Yusa