1 準備と講義概要
教員解説を聞きながら「一斉進行形式」で学びたい学生は 教室前方の教卓付近 に座ってください。講義資料を読みながら各自のペースで学び進めたい学生は最後列を含めて自由に座ってください。
授業の冒頭で「小テスト❷」を実施します。
GoogleColab にログインし、
PG1-第07回講義.ipynbという名前で「GoogleColabノートブック」を作成しておいてください。今回はノートブックの提出はありません。今回の講義では「数学計算に関する処理」と「繰り返し構文 (ループ変数の参照)」について学びます。これらを学ぶことで、タートルグラフィックスと組み合わせて次のような画像が描画できるようになります。
2 Mathライブラリ
各種数学の計算には mathライブラリ を利用します。
プログラムのなかで「mathライブラリ」を使用するためには、次のようにインポートが必要です。
特に、次の定数や関数は 利用頻度が高い ため覚えておいてください。
- 円周率 \(\pi\) (定数値)
math.pi # -> 3.141592
- 弧度法を度数法に変換 (rad \(\to\) deg)
- \(0.5\pi = 90^{\circ}\)
math.degrees(0.5*math.pi) # -> 90.0
- 度数法を弧度法に変換 (deg \(\to\) rad)
- \(-180^{\circ} = -\pi\)
math.radians(-180) # -> -3.1415...
- 階乗 \(_{n}\mathrm{C}_{r}\)
- \(5!=5\times 4\times 3\times 2\times
1\) …
math.factorial(5) # -> 120
- \(5!=5\times 4\times 3\times 2\times
1\) …
- 組み合わせの総数 \(_{n}\mathrm{C}_{r}\)
- \(n\) 個の異なるものから \(r\) 個を選ぶ「組み合わせ」の総数
- \(_{3}\mathrm{C}_{2}\) …
math.comb(3,2) # -> 3 - \(_{7}\mathrm{C}_{2}\) …
math.comb(7,2) # -> 21 - \(_{8}\mathrm{C}_{5}\) …
math.comb(8,5) # -> 56
このほかにも、mathモジュールには様々な関数が存在します。それらの一覧や詳細は リファレンス で確認してください。
組み合せ数の総数の求め方
\(n\) 個の異なるものから \(r\) 個を選ぶ「組み合わせ」の総数 \(_{n}\mathrm{C}_{r}\) は、1年次の「基礎数学B」で学習済みですが、次のように計算できます。
\[ {}_{n}\mathrm{C}_{r} = \frac{n!}{r!(n-r)!} \]
なお、上記の式はTeX (テキストセルで有効) では
$$ {}_{n}\mathrm{C}_{r} = \frac{n!}{r!(n-r)!} $$
のように表記します。
2.1 演習1 ( 目標時間: 12分)
確率 \(p\) の事象が、\(n\) 回の試行で \(k\) 回発生する確率 \(P\) について、次式 (二項分布) で計算できることが数学的に知られています。二項分布は、4年次の「確率統計」の授業で学びますが、高専2年でも十分に理解できる内容なので興味がある人はYouTube動画などで学んでください 。
\[ P={}_{n} \mathrm{C}_{k} \cdot p^{k}\cdot(1-p)^{n-k}\]
例えば「あたり」の割合が \(15\%\) のクジを \(5\) 回引いたときに、\(1\) 回だけ「あたり」がでる確率 \(P_{1}\) は、\(n=5\)、\(k=1\)、\(p=0.15\)として上式に代入して、次のように計算できます。
\[ P_{1}={}_{5} \mathrm{C}_{1} \cdot 0.15^{1}\cdot(1-0.15)^{5-1} \fallingdotseq 0.392\]
同様に、\(2\) 回だけ「あたり」がでる確率 \(P_{2}\) は次のように計算できます。
\[ P_{2}={}_{5} \mathrm{C}_{2} \cdot 0.15^{2}\cdot(1-0.15)^{5-2} \fallingdotseq 0.138\]
また、「\(1\) 回 あるいは \(2\) 回」の「あたり」がでる確率は \(P_{1}+P_{2}\) のように計算できます。
以上を踏まえて、以下の問題についてPythonプログラムを使って解いてください。
あるスマホゲームのガチャにおいて「メタスラの剣」というアイテムの提供割合(排出率)は \(0.5\%\) となっている。\(1\) 回のガチャには \(300\) ジェムが必要である。また \(10,000\) 円で \(11,720\) ジェムが購入できる。
- 問1:ガチャを \(100\) 回やって「メタスラの剣」が
1本も入手できない確率 (\(\%\)) を計算してください。
- ヒント:「1本も入手できない確率」とは、言い換えれば「0本入手できる確率」と同義です。
- 答え: \(60.58\%\)
注意
計算式に対して直接的に「数値 (数値リテラル)」を組み込まないでください。計算式は「変数」を使って構成し、数値は「変数」に対して与えるようにしてください。
%reset -f
import math
p1 = math.comb(100,0) * 0.005**0 * (1-0.005)**(100-0)
print(f'問1 {p1:.2%}') # .2% → 小数第2位までの百分率の書式指定子%reset -f
import math
p = 0.005 # 排出率
n = 100 # 挑戦回数
k = 0 # 入手数
p1 = math.comb(n,k) * p**k * (1-p)**(n-k)
print(f'問1 {p1:.2%}')なお、f文字列の「書式指定子」に %
が存在することは既に学習済みです。例えば
print(f'p={0.1519:.1%}') は p=15.2%
を出力します。
- 問2:ガチャを \(100\) 回やって「メタスラの剣」が
1本以上入手できる確率 (\(\%\)) を計算してください。
- ヒント:「1本以上入手できる確率」とは、言い換えれば「1本入手できる確率 + 2本入手できる確率 + … + 99本入手できる確率 + 100本入手できる確率」ですが、それを計算するのは愚かなので…。
- 答え:\(39.42\%\)
- 問3:\(100\)
人が挑戦したとき、「メタスラの剣」が1本も入手できない人は
(確率計算上では) 何人でてくるか計算してください。
- 答え:約 \(61\) 人
- 問4:\(100\)
回のガチャに挑戦するときに必要な金額 (日本円)
はいくらか計算してください。
- 答え:約 \(25,597\) 円
余裕がある学生は、前回講義で学習したGoogleColabのフォーム機能を利用して、UI (User Interface) / UX (User Experience) を強化したバージョンを作成してみてください。
3 組込み関数 (数学系)
組み込み関数とは、import
の宣言をせずに利用できる関数で「標準関数」や「ビルトイン関数」とも呼ばれます。既に何度も利用している
print や input
は組み込み関数になります。
ここでは、利用頻度が高い 数学系 の組み込み関数について記載します。
- 絶対値の計算
abs(-37564) # -> 37564abs(-10.9) # -> 10.9
- 大きい値の取得
max(55,100) # -> 100max(-55.5,-40.2,-75,-25) # -> -25
- 四捨五入の計算
round(15.863) # -> 16round(15.863,1) # -> 15.9
- べき乗の計算
pow(4,3) -> 64- 「**演算子」を使って
4**3とするほうが一般的です。
- 「**演算子」を使って
pow(38,-1,97)モジュラ逆数の計算 ( RSA暗号 (情報2の第09回講義で学びます) に関する計算などで使用 )- \((x\times38)\ \mathrm{mod}\ 97=1\)
となる \(x\) を求める計算 (\(\mathrm{mod}\) は Pythonの
%演算子に相当計算)
- \((x\times38)\ \mathrm{mod}\ 97=1\)
となる \(x\) を求める計算 (\(\mathrm{mod}\) は Pythonの
このほかにも、様々な組み込み関数が存在します。それらの詳細は リファレンス で確認してください。
3.1 とても重要なこと
「教わったこと」を教わった範囲で無条件に受け入れて終わるのではなく 好奇心や興味関心を持って、発展的に「試す」「調べる」「考える」に取り組めるように なってください。 例えば、次のような「疑問」や「興味」が湧いて、それに対して「試す」「調べる」「考える」ができるようになってください。
このような習慣が身に付いた人が、結果的に (4年生になる頃には) 高いプログラミングスキルを習得しています。
roundの第2引数に マイナスの値 (例えばround(15.863,-1)) を設定するとどうなるのか。maxという組み込み関数が存在するのなら 同様にminという関数も存在するのではないか。とりえずmin(55,100)を試してみよう。- 例示では
maxの引数に「数値」を与えていたけど 文字列を与えるとどうなるのか。実行時エラー?。それとも、文字数が多いほうを返す?。それとも・・・。
4 繰り返し構文におけるループ変数の参照
次のような C/C++言語プログラム をコンパイルして実行すると…
次のような出力を得ることができます。
i=0
i=1
i=2
i=3
i=4
一方で、Pythonでは 次のようなプログラムで同様の出力 を得ることができます。Colab.ノートブックに貼り付けて、実際に同じ出力が得られることを確認してください。
ここで、変数 i を 「ループ変数」や「ループカウンタ」
と言います。range(5) のとき、i
はループ毎に
0、1、2、3、4
に変化します。
ゼロオリジン (ゼロから開始、zero-based)
であるため 最終値が「5」ではなく「4」になる点
に注意してください。
4.1 ループ変数の参照
ループ変数は、繰返し構文の内部で 参照して (=読み取りして) 計算式に利用 したり、関数の引数に与えたり することができます。
%reset -f
import math
for i in range(5):
a = i + 5 # ループ変数 i を計算式に利用
b = math.sqrt(i) # ループ変数 i を関数の引数に利用
print(f'i={i}, i+5={a}, sqrt{i}={b:.2f}') # i+5 を出力
print('---') # for終了
print(f'i={i}') # for終了後も参照可能(最終値が格納されている)実行結果は、次のようになります。
i=0, i+5=5, sqrt0=0.00
i=1, i+5=6, sqrt1=1.00
i=2, i+5=7, sqrt2=1.41
i=3, i+5=8, sqrt3=1.73
i=4, i+5=9, sqrt4=2.00
---
i=4
ループを抜けた後も、第08行目 のように 変数 i が参照 (読み取り)
できること に注意してください。
4.1.1 演習2 ( 目標時間: 5分)
次のような標準出力を得たいとします。
X座標 : -4
X座標 : -3
X座標 : -2
X座標 : -1
X座標 : 0
X座標 : 1
X座標 : 2
X座標 : 3
X座標 : 4
次のプログラムの 第02行目 と 第04行目 を書き換えることで対応してください。
まずは 以下のプログラムを、そのまま実行して動作確認してから、そのあとに書き換えに取り組んでください。
- 補足: 第05行目 の
書式指定子
{x:>2}は「出力幅2 (右詰め・不足分は空白埋め) 」を意味します。 - 補足:
rangeの引数に手を加えて対応する方法もありますが、ここでは、その方法を使わずに目的の出力を得る方法を考えてみましょう。
演習が完了したら、次のプログラムでも同等の結果が得られることを確認してください。
4.1.2 演習3 ( 目標時間: 9分)
非負の整数値を格納する変数 n の値が \(N\) のとき、\(-N\)、\(-N+1\)、\(\cdots\)、\(0\)、\(\cdots\)、\(N-1\)、\(N\)
のような数列を得るようなプログラムを作成してください。
例えば、n が \(3\)
のときは次のような文字列を出力します。1文字のスペース区切り、正の値にはプラスの符号がついている、ゼロには符号を付けない点に注意してください。
-3 -2 -1 0 +1 +2 +3
例えば、n が \(5\)
のときは次のような文字列を出力します。
-5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
次のプログラムに追記する形式で作成してください。
%reset -f
#@markdown 0より大きい整数値を与えてください
n = 3 #@param {type:"slider", min:1, max:20, step:1}
# ここから先のコードを記述- ヒント: 前回講義で学んだ条件分岐構文 (if文) を利用してください (無論、それ以外の方法もあります) 。
- 中級者向けの指示: 最終値のあとにスペースが付加されないように してください。
- 補足: 02行目と03行目は フォーム機能 を利用している (第03回講義で学習済み) 。
4.2 等差数列の生成
初項 \(a\)、公差 \(d\) の等差数列の第 \(1\) 項から第 \(n\) 項は、次のプログラムで生成することができます。
実行結果は以下のようになります。
3 7 11 15 19 23 27 31
ループ変数 i はゼロオリジン (最初の値が \(0\) である)
ため、数学の教科書に記載されているような式
(1オリジン、one-based)
を参考にする場合は読み替えが必要です。
数学の教科書に記載されている等差数列の一般項
\[ a_{n} = a_{1} + (n-1)d \]
4.2.1 フォーム機能を使用して整形
上記プログラムを Colab.のフォーム機能 を使って整形・装飾したプログラム例を示します。Colab.ノートブックに貼り付けて実行してみてください。
%reset -f
#@markdown # **等差数列の出力**
#@markdown **初項** $a$ を入力してください。
a = 3 # @param {type:"number"}
#@markdown **公差** $d$ を入力してください。
d = 4 # @param {type:"number"}
#@markdown 第何項までの数列を出力するか設定してください。
n = 8 #@param {type:"slider", min:1, max:20, step:1}
for i in range(n):
print( a+d*i ,end=' ')
4.3 等比数列の生成
初項 \(a\)、公比 \(d\) の等比数列の第 \(1\) 項から第 \(n\) 項は、次のプログラムで生成することができます。
実行結果は以下のようになります。
3 6 12 24 48 96 192 384
等差数列と同様に、数学の教科書に記載されている式を利用する場合は「0オリジン」と「1オリジン」の違いに注意してください。以下は「微分積分1」の教科書に記載されている式です。
\[ a_{n} = a \cdot r^{(n-1)} \]
4.3.1 演習4 ( 目標時間: 6分)
上記の等比数列生成プログラムを Colabのフォーム機能 を使って整形・装飾したプログラムに変更してください (先に例として示した「等差数列」のフォーム対応プログラムをベースに作成してください)。また、その動作確認してください。
4.4 累積値の計算
for文は 累積値 を計算する場合にも利用されます。
例えば、次式のように定義される \(S\) は、
\[ S = 1^2 + 2^2 +3^2 +4^2 + 5^2 \]
以下のようなプログラムで計算ができます。
上記の 第04行目 の S = S + i**2
は S += i**2
のように省略表記できます。
なお S =+ i**2
では意図した動作をしないので注意してください。+=
のような演算子を「累算代入演算子」といいます。
4.4.1 演習5 ( 目標時間: 8分)
あるスマホゲームのガチャにおいて「URアイテム」の提供割合(排出率)は \(0.25\%\) となっています。
\(300\) 回のガチャをしたとき、「URアイテム」の入手個数が \(0\) 個の確率(\(\%\))、入手個数が \(1\) 個以上の確率(\(\%\))、\(2\) 個以上の確率(\(\%\))、\(3\) 個以上の確率(\(\%\))、\(4\) 個以上の確率(\(\%\)) を求めてください。
- ヒント
- \(1\) 個以上の確率 \(=\) 全確率 (\(1.0\)) \(-\) \(0\) 個入手の確率
- \(2\) 個以上の確率 \(=\) 全確率 (\(1.0\)) \(-\) ( \(0\) 個入手の確率+ \(1\) 個入手の確率 )
- \(3\)
個以上の確率 \(=\) 全確率 (\(1.0\)) \(-\) ( \(0\) 個入手の確率+ \(1\) 個入手の確率 + \(2\) 個入手の確率 )
- 先に示した累積値の計算を利用します。
- 答え
- 入手個数が \(0\) 個の確率 \(47.19\%\)
- 入手個数が \(1\) 個以上の確率 \(52.81\%\)
- 入手個数が \(2\) 個以上の確率 \(17.32\%\)
- 入手個数が \(3\) 個以上の確率 \(4.03\%\)
- 入手個数が \(4\) 個以上の確率 \(0.72\%\)
5 繰返し構文のネスト
条件分岐構文 (if-elif-else) と同様に、繰返し構文
(for) についてもネスト (入れ子)
が構成できます。このようなネストした繰返し構文を「多重ループ
(2重ループ)」や「多重繰り返し文
(2重繰返し文)」のようにいいます。
例えば「掛け算の九九の表」は、次のように2重ループで出力できます。
%reset -f
print('-'*27)
for x in range(9):
for y in range(9):
p = (x+1) * (y+1)
print(f'{p:>2d}',end=' ')
print('') # 1段ごとに改行
print('-'*27)実行すると次のような出力が得られます。
---------------------------
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
---------------------------
5.1 応用例1
第05回講義 で利用した タートルグラフィックス (Turtle Graphics) を再び使用します。独立したコードセルを作成して、以下を実行してください。これは、接続している仮想マシン (Linux) に「ColabTurtle」というPythonモジュール (ライブラリ) をインストールするコマンドになります。
!pip install ColabTurtle
Colab.の場合、インストールしたライブラリは、ウェブブラウザと仮想マシンと接続が切れると、仮想マシンと共に破棄されます (再度、Colab.ノートブックを開いたときは、再度、上記コマンドを実行する必要があります) 。
pip install とは (ChatGPT先生に聞いてみた)
pip install
はPythonのプログラミングツールのコマンドです。これを使うと、特定の機能を持つコードの集まり(ライブラリと呼ばれる)をダウンロードしてPythonプログラムのなかで利用できるようにすることができます。例えば
pip install pandas とすると、データ分析に使われる
pandas というライブラリをインストールできます。つまり
pip install
は、Pythonで新しい機能を手軽に追加するための道具として使用されます。
多重ループを利用して、次のようなドットマトリクスを描画してみます。
次のプログラムをColab.ノートブックに貼り付けて、実行してください。
%reset -f
import ColabTurtle.Turtle as t
# 初期設定
w, h = 400,200
t.initializeTurtle(initial_speed=13,initial_window_size=(w,h))
t.shape('circle');t.hideturtle();t.bgcolor(0,0,0)
t.width(12) # 軌跡幅(ペンの太さ)
# 2重繰り返し(2重ループ)
for x in range(19):
for y in range(9):
# 色設定 RGB
t.color(255-x*10, 255-y*25, 100)
# 点描画(現在位置から現在位置に移動軌跡を描く)
t.up(); t.goto(x*20+20,y*20+20)
t.down(); t.goto(x*20+20,y*20+20)ColabTurtleの詳細は、こちら を参照してください。
5.1.1 演習6 ( 目標時間: 8分)
上記の ドットマトリックスの描画.py
を書き換えて、次のようなドットマトリクスを描画してください。明るい赤は
t.color(255,0,0) で、暗い赤は
t.color(100,0,0) で色設定ができます。
- ヒント: 2重ループの内部で条件分岐構文を利用します。ゼロオリジンで考えて、0行目では3列目以降が暗い赤。1行目では5列目以降が暗い赤。2行目では7列目以降が暗い赤。では、y行目では何列目以降を暗い赤にすべきか? → \(2*y+3\)
5.2 応用例2
多重ループを使うことで、次ようなサイバーなロゴを描くこともできます。
上記を描くためのプログラムは以下のようになります。
同心多角形の描画の観点では第34行目がポイントとなります
(1年の「基礎数学」で学んだ三角関数の実用例です。例えば、第34行目を
t.goto(cx-r/2,cy+r/2) に書き換えると
(実際に試してみて!)
美しい同心多角形にならないことが分かると思います) 。
%reset -f
import math
import ColabTurtle.Turtle as t
n=6 # N角形
r=50 # 一片の長さ
cx,cy = 250,200 # 中心座標
# 初期設定
t.initializeTurtle(initial_speed=13,initial_window_size=(cx*2,cy*2))
t.shape('circle');t.hideturtle();t.bgcolor(0,0,0)
# N角形の外角を計算しておく
deg = 180-360/n
rad = math.radians(deg)
# 色の初期値
red = 255
pen = 10
# 文字
t.color(red,0,0)
t.up()
t.goto(cx, cy+6)
t.write('ERROR',align='center',font=(20,'Arial','bold'))
# 2重ループ
for p in range(10):
t.up()
t.width(pen)
t.color(red,0,0)
t.goto(cx, cy)
t.setheading(0) # 向きを0°(3時方向)に設定
t.goto(cx-r/2,cy+math.tan(rad/2)*r/2)
t.down();
# N角形を描画
for i in range(n):
t.forward(r)
t.left(180-deg);
# 辺の長さと色を更新
r+=18
red-=255.2.1 演習7
美しく魅力的な画像を生成せよ。
%reset -f
import ColabTurtle.Turtle as t
# 初期設定
w, h = 500,500
t.initializeTurtle(initial_speed=13,initial_window_size=(w,h))
t.shape('circle');t.hideturtle();t.bgcolor(0,0,0)
t.width(1)
# 2重繰り返し(2重ループ)
for i in range(40):
t.up();
t.goto(20,10*i+20)
t.down();
t.goto(410-10*i,20)6 数学に基づく計算 v.s. PGによるゴリ押し計算
「微分積分1」の授業では、等比数列の第 \(1\) 項から第 \(n\) 項までの和 \(S_{n}\) が、次式で計算できることを学びました (数学的な証明についても示されたはずです) 。
\[ S_{n} = \frac{a(1-r^{n})}{1-r}= \frac{a(r^{n}-1)}{r-1}\]
一方で、プログラムで繰返し構文を使ったシンプルなゴリ押し計算でも \(S\) を求めることができます。
両者について比較・考察してみたいと思います。例として、初項 \(a=16\)、公比 \(r=-1/2\) の初項 (第 \(1\) 項) から第 \(50\) 項までの和 \(S_{50}\) を求めてみます。
6.1 計算の正確度を比較
Colab. のノートブックを使って実際に、2つのプログラムを実行して、その結果を確認してみてください。
非常に小さな値ですが計算結果が違っていることが分かると思います。証明は省略しますが、数学公式を使って計算した結果のほうが正しい (正確な) 答えになります。
プログラムでゴリ押し計算した値にズレが生じるのは「情報2」の授業で習ったように 丸め誤差 と 桁落ち誤差 の影響によるものです (情報2の講義ノートの p.76-77 を参照)。公式を使った場合でも、それら誤差は避けられませんが、繰返し構文よって累積しないため影響が小さく抑えられています。
6.2 計算時間を比較
GoogelColab.では %%timeit
というマジックコマンドを使ってコードセル単位の実行時間を計測することができます。
%%timeit
では、何回もコードセルを実行し、その実行時間の平均と標準偏差
(=「情報1」で学習したように「ばらつき」の指標 )
を出力します。そのため、%%timeit を使用する場合には
print
などの出力はコメントしておきます。また、%reset -f
もコメントアウトしておきます。
次のコードを Colab. に貼り付けて実行してみてください。
%%timeit
# %reset -f
a = 16
r = -1/3
n = 50
S = 0
for i in range(n):
S += a*r**i
# print(f'S = {S:.20f}')実行結果の一例を示します。これらの実行結果は、各自に割り当てられた仮想マシンのスペックに依存するので注意してください。
「method-1b.py」の実行結果
208 ns ± 9.64 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
「method-2b.py」の実行結果
9.63 µs ± 3.1 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
これは \(1,000,000\) 回のループを \(7\) 回実行したとき、その \(7\) 回の実行についての 平均実行時間 が \(208(\mathrm{ns})\) で、その標準偏差 が \(9.64(\mathrm{ns})\) であった、という意味になります。