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ループを使えば、

In [1]:
total = 0
for i in range(1, 11): #11は含まれない
    total += i    
print(total)
 
55
 

 となる。3行目のtital += iは、total = total + iの省略形である。他にもa -= ba *= bなどのように略すことができる。それぞれa = a - ba = a * bを略したものである。ループを回したい範囲はrange(start, stop, step)で指定する。ただし、stopは含まれないので注意。上の例のようにstepを省略すると1と解釈される。

 forループを使わずに、NumPyを使えば、

In [3]:
import numpy as np
a = np.arange(1, 11)
total = np.sum(a)
print(total)
 
55
 

のようにaを配列として定義して和np.sumを取ればいい。むしろこの方が高速に計算できる。

 

(例) $n=10$のとき、$n!$を求めよ。
 練習のためforループを使えば、

In [4]:
n = 10
fact = 1
for i in range(1,n+1):
    fact *= i #fact = fact * iの略
print(fact)
 
3628800
 

 一方、NumPyには、数列の積を求めるnp.prodというfunctionが用意されているので、この場合もNumPyを使う方が計算速度が速くて簡単である。

In [5]:
a = np.arange(1,n+1) 
fact = np.prod(a)
print(fact)
 
3628800
 

 また、math moduleには階乗を計算するfunctionがある。

In [5]:
import math 
print(math.factorial(n))
 
3628800
 

 

 forループの書式はrangeを使う以外もたくさんある。例えば、配列aというものが下のように定義されていたとする

In [6]:
a = np.arange(3, 6)
print(a)
 
[3 4 5]
 

これをさきほどのrangeを使って

In [7]:
for i in range(3):
    print(a[i])
 
3
4
5
 

としてもいいし、簡単にaの配列の要素分だけforループを回すという意味で、

In [8]:
for n in a:
    print(n)
 
3
4
5
 

としてもよい。この場合のnaの要素に対応する。さらに以下のように、inのあとはlistを置くこともできる。

In [9]:
list_a = ['Beaf', 'Egg', 'Milk']
for n in list_a:
    print(n)
 
Beaf
Egg
Milk
 

 配列のように、いくつかの要素をひとまとめにしたもののことをコンテナオブジェクトと呼ぶが、このコンテナオブジェクトに渡ってforループを回すことが多いので、for n in 変数名のような形式のforループはpythonでよく使われる。実は文字列そのものもコンテナオブジェクトなので、以下のような使い方もできる。

In [10]:
name = "test"
for n in name:
    print(n)
 
t
e
s
t
 

 コンテナオブジェクトをenumerateすれば、インデックス番号と要素をそれぞれ取り出すこともできる。enumerateは日本語で「列挙する」という意味。

In [11]:
for n, item in enumerate(list_a):
    print(n, item)
 
0 Beaf
1 Egg
2 Milk
 

 数値計算の場合はNumPyを使うので、一般的にはなるべくforループを使わないことをお勧めするが、このfor n in 配列名:や、for n in enumerate(配列名)のような書き方は一般的なpythonのコードでよく見かけるので、使いこなせるようになるとプログラミングの幅が広がる。

 

 

whileループ

 forループはループの回数をあらかじめ指定しなければいけないが、回数は決めずにある条件を満たすまではずっとループを回したい、という場合も多い。その場合はwhileループを用いる。whileの後に条件を書けばよい。

In [12]:
i = 0
while i < 100:
    i += 1
print(i)    
 
100
 

 条件の書き方は==, <=, などがある。複数の条件がある場合は、andorで結べばよい。0 < i < 3のような書き方もできる。詳細はif文のところで取り上げる。

In [13]:
i = 3
while 0 < i < 5:
    print(i)
    i += 1
 
3
4
 

 

練習問題

(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ループを使うなら、

In [14]:
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)
 
2.71828179834636
3.0112685234229275e-08
 

 pythonのforループは遅いので、数秒程度(かそれ以上)計算に時間がかかる。

 もっと簡単なのは、単純にべき乗**を使う方法だろう。

In [15]:
n = int(1e8)
e = (1 + 1/n)**n
print(e)
print(np.e - e)
 
2.7182817983473577
3.011168736577474e-08
 

 二つの方法とも大体同じ値になっている。(が微妙に差がある。これはアルゴリズムの違いによる誤差で、誤差の評価は改めて考える。) 大体8桁の精度が出ている。$n=10^8$ぐらいまで大きくすると、おそらくforループの遅さを体感できるに違いない。

 

 

(1.9.2.) 桁数は$5$桁までと指定されているが、何回ループを回してよいかは自明でないので、whileループを使う。桁を調べるには、求めたフィボナッチ数の$\log_{10}$(np.log10)を取って、$5$より小さいときにはwhileを回すようにする。

In [2]:
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)
 
[    0     1     1     2     3     5     8    13    21    34    55    89
   144   233   377   610   987  1597  2584  4181  6765 10946 17711 28657
 46368 75025]
 


当サイトのテキスト・画像の無断転載・複製を固く禁じます。
Unauthorized copying and replication of the contents of this site, text and images are strictly prohibited.
© 2019 Go Yusa