1 準備
- GoogleColab.
にログインしておいてください。
- 「0629_繰返し構文とリスト.ipynb」という名前でノートブックを作成しておいてください。
- 今回はノートブックの提出はありません。
2 前回の復習: Pythonの実行開発環境の構築
操作上の不明箇所があれば、まずは前回の 第09回講義ノート を参照してください。
- ローカル環境に
0629-PG1という フォルダを新規作成 してください (フォルダ名は任意に設定可) 。 - 作成したフォルダを VS Code で開いてください (コンテキストメニューから「Codeで開く」を選択)。
- VS Code 内で
Ctrl+Shift+@でターミナル (PowerShell:pwsh) を起動してください。 - Pythonの仮想環境を作成し、その仮想環境を
有効化 してください。
python -m venv .venv.venv/Scripts/Activate.ps1
pip listで仮想環境にインストールされているPythonライブラリ (Package) を確認してください。次のように最低限のライブラリ (Package) だけがインストールされているはずです。
Package Version
---------- -------
pip 23.1.2
setuptools 65.5.0
- 次のようなPythonプログラム (
test01.py) を VS Code 上で作成して、正常に実行できることを確認してください。
- ノートブック開発環境 Jupyter を VS Code
でも使えるようにするために
pip install jupyterで jupyter ライブラリを Python仮想環境 にインストールしてください。インストール後はpip listでライブラリが追加されていることを確認してください。 - VSCode上で
test02.ipynb(拡張子に注意) を作成し、コードセルを追加し、次のプログラムを記述してください。
%reset -f
print('ドラゴンクエストIII そして伝説へ…')
- ノートブック編集画面の右上の「カーネルの選択」から適切なもの
(Python仮想環境) を選択して、その後、コードセルを実行
(
Ctrl+Enterを押下) して、適切な出力が得られることを確認してください。
2.1 Google Colab. にプリインストールされているパッケージの確認
Coogle Colab. では 主要なPythonライブラリ (Package ) が予めインストール されています。Coogle Colab. にインストール済みのライブラリを確認するために、Colab.でコードセルを追加して、次のようなコマンドを打ち込んで実行してください。
!pip list
Colab. においては ! からはじまる文は Pythonプログラムではなく OS (Linux)
に対するコマンド として扱われます。
3 pipコマンドを使ったライブラリの追加
pipコマンドを使った Pythonライブラリの追加 について学んでいきます。pipは「ピップ」と読みます。
pipとはなにか
pip (ピップ) とは、Pythonの「パッケージ管理システム」のひとつで、Pythonで書かれたパッケージ のインストール/管理を行なうためシステムです。パッケージ (ライブラリ・モジュール) とは、他の人が書いて再利用可能にしたPythonのコードのことを指します。これらのパッケージを使用すると、自分ですべてのコードを書く代わりに、他の人が既に書いたコードを利用することができ、作業を効率化できます。
本授業では利用しませんが、Python開発環境の Anaconda (アナコンダ) では conda というパッケージ管理システムが採用されています。なお、pip と conda を併用すると様々な問題が生じるので注意してください。
3.1 準備
ローカルPC上に構築したノートブック環境 (Jupyter環境)
において、コードセルを追加して、次のプログラムをコピペして実行
(Ctrl+Enterを押下)
してください。以下のコードは matplotlib
というライブラリを利用してグラフを描画
するプログラムになります。
%reset -f
import matplotlib.pyplot as plt
x = [0, 10,20,30,40]
y = [20,40,30,50,40]
fig,ax = plt.subplots()
ax.plot(x,y,marker='o')
plt.show()実行すると
ModuleNotFoundError: No module named 'matplotlib'
という実行時エラーが発生します。このエラーメッセージは、上記の第02行目の
import に関連して発生しているもので ModuleNotFoundError: ‘matplotlib’ という名前の
モジュール (=ライブラリ、パッケージ) が見つかりません
という意味です。
現在のPython環境 (=先ほど作成・有効化した仮想環境内) に、matplotlib のライブラリが存在しないために生じているエラーになります。
3.2 現在の開発環境にインストールされているライブラリの確認
念のために「本当に matplotlib が現在のPython環境
(=venvで構築した仮想環境)
にインストールされていないのか?」を確認してみましょう。
VS Code のターミナルから、次のコマンドを入力して インストールされているライブラリを一覧表示 してください。
pip list
結果が大量出力されます。このなかから matplotlib
の有無を探すのは大変なので、出力結果から mat
という文字を含む行だけを抽出して表示します。
次のコマンドを入力してください。
pip list | Select-String -Pattern "mat"
ここで | は「情報1」で学習したように パイプ
と呼ばれる記号です。コマンドライン上で、パイプを
command1 | command2
のように使用したときは、command1 の出力を
command2
の入力に「パイプする」という意味になります。
「パイプする」とは、コマンドの実行結果 (文字列の出力) をコンソールに表示するのではなく 別のコマンド対して入力として引き渡す ということを意味します。
ここでは pip list というコマンドの実行結果
(文字列の出力) を、Select-String -Pattern "mat"
というコマンドに対する入力として与えるという意味になります。
このコマンドの結果は、次のようになります。
(.venv) PS C:\Users\wada\...\0629> pip list | Select-String -Pattern "mat"
matplotlib-inline 0.1.6
nbformat 5.9.0
この結果から確かに matplotlib
はインストールされていないことが確認できます。
3.3 pip本体の更新
pipコマンドを使って「Pythonライブラリ」をインストールする際には、まずはじめに pip 本体が最新版であることを確認 する必要があります。以下のコマンドにより、pipのバージョンをチェックし、もし古いバージョンであれば最新版に更新すること (アップグレードすること) ができます。
python -m pip install --upgrade pip
もし、現状でpipが最新版であれば、以下のようなメッセージが表示されます ( Requirement already satisfied : 要求は満たされている )。
Requirement already satisfied: pip in c:\users\wada\...\0629\.venv\lib\site-packages (23.1.2)
また、最新版でなかった場合は、、以下のようなメッセージが表示され、pipが最新版に更新されます。特に最終行が「Successfully」となっていることを確認してください。問題があるとエラー関係の表示に出力されます。
Requirement already satisfied: pip in c:\users\wada\...\0629\.venv\lib\site-packages (22.1)
Collecting pip
Downloading pip-23.1.2-py3-none-any.whl (2.1 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 26.3 MB/s eta 0:00:00
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 22.1
Uninstalling pip-22.1:
Successfully uninstalled pip-22.1
Successfully installed pip-23.1.2
このようなアップグレード操作をしておかないと 以降の操作を行なった場合にエラーが発生する可能性があります。この点に注意してください。
3.4 Pythonライブラリのインストール
以下のコマンドで matplot
をインストールすることができます。pip installを実行するとインターネット上の
PyPI(パイピーアイ、Python
Package Index)という
Pythonのパッケージのリポジトリ
から目的のライブラリを検索し、それをローカル環境にダウンロードしてインストールします。
pipによるライブラリのインストールは、スマートフォンにおいて Playストア や AppStore などのリポジトリからアプリをインストール するイメージです。
pip install matplotlib
上記のコマンドを実行すると matplotlib
以外にも、依存関係のあるライブラリ
(matplotlibの動作に必要な別ライブラリ)
もあわせてインストールされます。
完了後、再度
pip list | Select-String -Pattern "mat"
を実行し、実際にインストールができたか確認してみてください。
3.5 動作確認
VS
Codeで作成しているノートブックに戻って、再度、セルを実行してください。今度は
ModuleNotFoundError
が発生せず、プログラムが実行され、次のようにグラフが出力されるはずです。
ModuleNotFoundError
今後、ModuleNotFoundError
が発生した場合は、ここで説明した手順で必要なライブラリをインストールしてください。
4 繰返し構文 (少しだけ発展的な利用)
4.1 繰返し構文の基本
ここまで、繰返し構文としては、次のように
range に 1個の引数
(以下の例では 5)
を与えるものを取り扱ってきました。
このプログラムを実行すると、まずは
ループ変数である i に 0
が格納された状態でブロック
(=インデントされた範囲) が実行され、つづいて i に
1
が格納された状態でブロックが実行され・・・という処理が5回、つまり
変数i が 4
となるまで処理が繰返されました。
0
1
2
3
4
4.2 演習①
次のプログラムの第03行目を書き換えて、下記に示す「期待する結果」が得られるようにせよ。
- Colab.環境 (ローカル環境PCのJupyter環境を含む)
以外で実行する場合は
%reset -fを削除すること (ノーマルなPython環境では%reset -fはエラーになります) 。
期待する結果
5
6
7
8
9
4.3 rangeに対して「2つの引数」を与える
演習①のような方法で 5 から 9
を得ることはできます。
この方法とは別に range
に「開始値」と「終了値+1」の
2つの引数
を与えることでも同様の処理が可能となります。
range に2個の引数を与えた場合、ループ変数
(上記の例では i) には「第1引数の値」から「第2引数の値
未満
の値」が順次格納され、ループ処理されます。
range(10,5)のように 第1引数\(>\)第2引数 とした場合、どのようになるか。結果について推測したうえで、実際にコードを実行して確認せよ。range(-5,5)のように引数に負数を与えた場合、どのようになるか。結果について推測したうえで、実際にコードを実行して確認せよ。range(0.5,9.5)のように引数に小数値を与えた場合、どのようになるか。結果について推測したうえで、実際にコードを実行して確認せよ。range(5,20,2)またrange(5,20,4)としてコードを実行し、第3引数の値がどのような意味を持つかを考察し、その考察が正しいものかコードを実行して検証せよ。また、rangeの第3引数の意味についてウェブで調査せよ。range(5,20,-1)またrange(5,20,-2)として実行し、第3引数の値がどのような意味を持つかを考察し、その考察が正しいものかコードを実行して検証せよ。また、rangeの第3引数の意味についてウェブで調査せよ。
5 繰返し構文 (ループ変数に小数値を使用したい場合)
上記では range
の引数により、ループ変数の「範囲 (開始値・終了値)
」や「刻み幅」を(ある程度までは)操作できること学びました。しかし、range
では整数値しか扱うことができず、小数値をループ変数したい場合
(特に物理シミュレーションなどの要求)
は、次のようにブロック内で別の変数を用意する必要があります。
次のプログラムを実際に実行して結果を確認してください。
%reset -f
# ループブロックで 0.00 から 1.00 まで
# 0.01 刻みの値を使用したい
for i in range(100+1):
t = 0.01 * i
print(f'{t:.2f}',end=' ')
if i%10 == 0: # 0.1単位で改行
print('')上記では、割り算の「余り」を求める
%
演算子を使用して、一定値に達したときに改行するようにしています。
プログラミングにおいては、可能な範囲で減らせる変数は減らせることが望ましく
(使用する変数は必要最低限にすることが望ましく)
、上記のような要求は numpy
というライブラリを使うことで解決することができます。
numpy
は数値計算支援用のライブラリで「ナムパイ」ではなく「ナンパイ」と読みます。工学分野では極めて使用頻度が高いライブラリとなります。具体的には
range() の代わりに np.arange()
を使うことで、直接的に小数のループ変数を使用することができます。
%reset -f
import numpy as np # numpy を np という省略名で使用
for t in np.arange(0,1.01,0.01):
print(f'{t:.2f}',end=' ')
if t % 0.1 == 0 :
print()実際に実行して結果を確認してください。
5.1 演習②-1
上記プログラムを実行すると、次のように期待するような位置で改行がされていない。なぜ、このようなことが発生するのか、分析・考察せよ。また、どのように書き換えれば、期待するような結果が得られるか考えてコードを修正せよ。
0.00
0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.10
0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.20
0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40
0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.50 0.51 0.52 0.53 0.54 0.55 0.56 0.57 0.58 0.59 0.60 0.61 0.62 0.63 0.64 0.65 0.66 0.67 0.68 0.69 0.70 0.71 0.72 0.73 0.74 0.75 0.76 0.77 0.78 0.79 0.80
0.81 0.82 0.83 0.84 0.85 0.86 0.87 0.88 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99 1.00
ヒント (現象の原因) :
tの値とあわせてt % 0.1の値を出力して、考察の材料としてみること。小数値の2進数表現の問題は何か。2進数では情報2の第03回講義ノート p.58 参照。ある程度、考察してみても理解できなければ、とりあえず、原因追及は諦めて解決する方法を考えてみること。ヒント (現象の解決法):様々な方法があるが、例えば
t % 0.1 == 0の部分書き換えで対応することができる。答え (現象の解決法):例えば
if t*100 % 10 < 0.1 :のように書き換えれば期待する出力が得られる。
5.2 演習②-2
上記のプログラムの第02行目を import numpy as np
から import numpy に変更した場合 (=np
という省略形を使わない場合)、以降のプログラムをどのように書き換えるべきか。実際にコードを記述して確認せよ。
6 繰り返し構文2 (whileの利用)
ここまでは、繰返し構文として for
を学習してきました。for
を使った繰返し構文は、繰返しの回数が予め決まっている場合
に使いやすいものとなっています。
一方で、繰返し回数が決まってなく、特定の条件が満たされるまで繰返し処理したい場合には
while による繰返し構文が適しています。
while
の一例を示します。次のプログラムをコピペして実行し、その後、その実行結果とプログラムを照らし合わせて内容を理解してください
(重要なポイントになります、読み飛ばさず、確実に理解してください)
。理解できないところは「Python
while 初心者向け」などでウェブ検索して解決してください。
%reset -f
import time # 第12行目で time.sleep(3) を使用する準備
import random as r
print('九九の練習プログラムです')
input('はじめるためには [Enter] を押下してください。')
x = 1 # 処理継続フラグを立てる
while x == 1 : # 処理継続フラグ変数 x が 1 である間ずっと (while)
a = r.randint(1,9) # 1~9の範囲のランダムな整数値
b = r.randint(1,9)
print(f'問題:{a} x {b} = ? (3秒後に答えが表示されます)')
time.sleep(3) # 3秒間プログラムを停止
print(f'答えは「{a*b}」でした。')
u = input('つづける場合は [Enter]、終了する場合は 何か文字を入力して [Enter] > ')
if u != '' :
x = 0 # 処理継続フラグを折る
print('お疲れ様でした。')while
では、条件式が成立する間、ブロック内の処理が継続されます。条件式は
if と同じように while x < 10 : や
while -10 <= x <= 10 :、while x != y :
のように記述することができます。
while
構文では、初回も条件式が成立するかチェックされます。そのため、条件の与え方によっては、1回もループ処理されない可能性があります。
6.0.1 演習③
while
による繰返し構文を利用して、次のような値を出力するプログラムを作成せよ。
5 6 7 8 9 10 11 12 13 14 15
ヒント: 次のプログラムを参考にせよ。
6.1 無限ループ
while 1 : または while True :
のようにすると無限ループとなります。無限ループから抜けだすためには
break 文を使用します。
次のプログラムをコピペして実行し、その後、その実行結果とプログラムを照らし合わせて内容を理解してください。
%reset -f
import random as r
import time
wallet = 20_000 # 所持金
bet = 300 # ガチャ1回の費用
n = 1
# SSRゲット or 破産するまでの無限ループッ
while True :
print(f'{n}回目の{bet}円ガチャに挑戦',end='')
wallet -= bet #「wallet=wallet-bet」と同じ
for i in range(3):
time.sleep(0.5)
print('.',end='')
x = r.choices(['R','SR','SSR'],weights=(75,20,5))[0]
print(f'{x}を入手! 残金は{wallet:,}円{"っ"*r.randint(1,5)}!')
if wallet < bet or x == 'SSR' :
break
n += 1 # 「n=n+1」と同じ 6.1.1 補足: random.choices
random.choices
は、次のように結果を必ずリストとして返してきます。リストではなく、リストの中身が必要な場合は
[] で要素を取得してください。
%reset -f
import random as r
# キーワード引数 k=4
x = r.choices(['R','SR','SSR'],weights=(75,20,5),k=4)
print(x) # => ['R', 'SR', 'R', 'R']
# キーワード引数 k=2
x = r.choices(['R','SR','SSR'],weights=(75,20,5),k=2)
print(x) # => ['R', 'R']
# キーワード引数 k=1
x = r.choices(['R','SR','SSR'],weights=(75,20,5),k=1)
print(x) # => ['R']
# キーワード引数 kを省略(k=1)と同じ
x = r.choices(['R','SR','SSR'],weights=(75,20,5))
print(x) # => ['R']
# キーワード引数 kを省略(k=1)と同じ
x = r.choices(['R','SR','SSR'],weights=(75,20,5))
x = x[0] # xはリストなので x[0] でゼロ番目の要素を取得
print(x) # R
# 省略形
x = r.choices(['R','SR','SSR'],weights=(75,20,5))[0]
print(x) # R6.2 演習③
自キャラと敵キャラによる1対1の自動バトル (交互に攻撃して決着がつくまで戦う系) を実況するようなプログラムを作成せよ。なお、詳細に作りこむととキリがないので、授業中は15分程度で次のセクションに移ること。
- ここでは
whilebreakを使用することを期待しています。
作成例
勇者ヨシヒコの前にスライムがあらわれた。
勇者ヨシヒコのHP「50」、スライムのHP「8」。
勇者ヨシヒコの攻撃、スライムに3のダメージ!! スライムのHP 8 -> 5
スライムの攻撃、勇者ヨシヒコに2のダメージ!! 勇者ヨシヒコのHP 50 -> 48
勇者ヨシヒコの攻撃、スライムに3のダメージ!! スライムのHP 5 -> 3
スライムの攻撃、勇者ヨシヒコに・・・
7 リスト操作の負荷量の比較
前々回の講義ではリストを初期化する方法 (例えば
[0]*10 など)
や、リストに要素を追加する方法 (例えば
append や insert メソッドなど)
を学習しました。
基本的に、リストに順次要素を追加する方法は、いわゆる「重たい処理」となります。あらかじめリストの長さが決まっているケースでは、その長さのリストを作成しておき、そこに値を書き込んでいくほうが高速かつ効率的な処理となります。
例として、長さが「1000」で、その要素として先頭から
0、1、2、…、999
という数値が格納されるリストを生成すること考えます。この処理を次の3つの方法で実行して、それに要する時間を
%%timeit で計測してみます。
%%timeit は既に過去の紹介していますが、セル単位の実行時間を計測するマジックコマンド
(Colab.環境専用) です。なお、%%timeit
のセルのなかでは %reset -f や print
は使わないようにしてください。
次の3つの処理の処理時間を比較します。Colab.環境で上記の各プログラムを実行して、その実行時間を比較してみてください。
- 初期値が 0 で、要素数 1000 のリストを作成する
(
arr=[0]*1000) 。その後、for構文で、先頭から順番に要素の値を書き換えていく。 - 要素数が 0 の空のリストを作成する
(
arr=[])。その後、for構文のなかで、リストの末尾に順次要素を追加していく。 - 要素数が 0 の空のリストを作成する
(
arr=[])。その後、for構文のなかで、リストの先頭に順次要素を追加していく。
特に insert を使ってリストの途中 (末尾以外)
に要素を追加する処理は、処理時間が長くなることが確認できると思います。
8 リスト要素の削除
リストは append や intsert
などのメソッドにより「要素の追加」が可能でした。これに対して
pop や remove
などのメソッドにより「要素の削除」が可能です。
リストの要素削除について動作を確認するために、次のようなリストを考えます。実行して結果を確認してください。
%reset -f
arr = list('ABCDBBEF') # 文字列からリストを作成可能
print(arr) # => ['A', 'B', 'C', 'D', 'B', 'B', 'E', 'F']
print(len(arr)) # => 8上記のリスト arr
に対して要素削除の操作を行ないます。
pop
は「指定したインデックスの要素」を取り除き、その部分を「前詰め」します。例えば、arr
から 'C'
を取り除きたいときは、次のプログラムのように、そのインデックスである
2 を pop
の引数に指定します。pop メソッドには
戻り値
があり、取り除いた値を受けとることもできます。
実際に次のプログラムを実行して、結果を確認してみてください。
%reset -f
arr = list('ABCDBBEF')
p = arr.pop(2)
print(arr) # => ['A', 'B', 'D', 'B', 'B', 'E', 'F']
print(len(arr)) # => 7
print(f'p={p}') # => p=C上記の例では、第03行目で変数 p で
arr.pop(2)
の結果を受け取っていますが、これは、次のように受け取らないことも可能です。
上記のプログラムでは、取り除きたい要素 'C'
のインデックスを 2
のように数値で直接的に指定しました。しかし、実施にはそのようなケースは少なく、前々回で学習した
index メソッドと組み合わせて利用します(
index
は指定の要素が「リストの何番目に存在するかを調べるメソッドでした)。
つまり、次のように利用します。
これは、さらに次のように変数 idx
を省いて記述することができます。可読性が損なわれない範囲で、不要な変数は省くことが望ましいです。
popの引数に負数を与えたとき、どのようになるか。結果について推測したうえで、実際にコードを実行して確認せよ。
8.1 remove による要素の削除
remove
では、インデックスではく、削除したい要素そのものを指定してリストから取り除くことができます。先ほどと同様に
'C' を削除したい場合は次のようにします。
- リストに存在していない要素 (たとえば
Zなど) をremoveの引数に与えたとき、どのようになるか。結果を推測したうえで、実際に結果を確認せよ。 removeの戻り値はどのようになるか。結果について推測したうえで、実際にコードを実行して確認せよ。
remove
では、リスト内で見つかった最初の要素のみ(1個のみ)
を削除します。全てを削除したい場合は while
などと組み合わせて使用する必要があります。
次のプログラム実行し、その結果を確認してください。
8.2 演習④
次のプログラムが意図する動作をするようにコードを追加せよ。
- ここでは
whileを使用することを期待しています。 - ここではリストの
removeメソッドを使用することを期待しています。 - リストのなかに特定の要素が存在するかの条件式は
inで与えることができました。第08回ifに設定することができる条件式は、whileに対する条件式として使うことができます。
%reset -f
import random as r
target = 'やくそう'
items = ['どうのつるぎ','やくそう','どくけしそう','ぬののふく', 'やくそう', 'やくそう', 'かわのたて']
print('勇者ヨシヒコの所持品 : ',end='')
print(*items, sep=' ')
print()
print(f'勇者ヨシヒコはパルプンテを唱えた。なんと全ての「{target}」が粉々に砕け散った。')
print()
# ここに items からすべての target を取り除く処理を記述する
print('勇者ヨシヒコの所持品 : 'リストのアンパック
リストを関数の引数として与える場合、先頭にアスタリスクをつけて アンパック (展開) という処理を施すことができます。これにより、次のようなことができます。
%reset -f
items = ['どうのつるぎ','やくそう','どくけしそう','ぬののふく']
print(*items, sep='\n') # itemsの頭に「*」を付けた。また sep 引数を設定このとき、第03行目のは次のような文と同じ意味を持ちます。
つまり、リストをアンパックすると「リスト内の要素を個別の引数として関数に与えること」が可能になります。
9 宿題
次回は、次のような「斜方投射」の簡易シミュレーションを扱います。斜方投射 (基礎物理学) について復習し、また、下記のプログラムについて読解を試みてください (分からなくても60分は費やしてください) 。
ある時刻において位置 \(x (\mathrm{m})\)、速度 \(v(\mathrm{m/s})\) の物体がある。この物体について、微小時間 \(dt\) 秒後の位置を求めよ。答え:\(x+v\cdot dt\)
ある時刻において速度 \(v(\mathrm{m/s})\)、加速度 \(-g(\mathrm{m/s^2})\) の物体がある。この物体について、微小時間 \(dt\) 秒後の速度を求めよ。答え:\(v-g\cdot dt\)
%reset -f
import math
import numpy as np
import matplotlib.pyplot as plt
# パラメータ設定
v0 = 20.0 # 初速度(m/s)
theta = 60 # 水平方向から上方にθ(deg)
g = 9.8 # 重力加速度m/s^2
# シミュレーション
dt = 0.1 # 位置計算の時間間隔
iter = 80 # シミュレーション繰返し回数
theta = math.radians(theta) # deg -> rad
arr_x=[0]*iter # X位置を格納するリストの初期化
arr_y=[0]*iter # Y位置 〃
arr_x[0] = x = 0 # Xの初期位置
arr_y[0] = y = 0 # Yの 〃
vx = v0 * math.cos(theta) # X成分の初期速度
vy = v0 * math.sin(theta) # Y成分 〃
# シミュレーション
for i in range(1,iter):
vy = vy - g * dt # Y方向の速度更新
x = x + vx * dt # X位置の計算
y = y + vy * dt # Y位置 〃
# 地面衝突の反発 (バウンド) の処理
if y < 0:
y = 0
vy = -vy * 0.8 # 反発係数
arr_x[i] = x
arr_y[i] = y
# 可視化(現状でここから先は理解不要)
fig,ax = plt.subplots(dpi=120)
ax.scatter(arr_x,arr_y,marker='o',s=20,alpha=0.5)
ax.axhline(0,c='black',lw=0.5)
ax.set_aspect('equal', adjustable='box')
ax.set_ylim(-1,20)
plt.show()10 課題04の解答例
10.1 問題
FC版DQ3 (ファミコン版のドラゴンクエスト3)
の武器を扱った「100連ガチャ」のシミュレータを作成したい。ガチャにより排出される武器は
item_names_str
でカンマ区切りで与えられる全33種の武器である。また、その排出比は
item_p_str
でカンマ区切りで与えた数値となっている。
ここでの排出比とは・・・例えば「ひのきのぼう」「こんぼう」「どうのつるぎ」の全3種を仮定し、その排出比が「\(10\)」「\(5\)」「\(2\)」であれば、「ひのきのぼう」があたる確率は \(10/(10+5+2)\)、「こんぼう」があたる確率は \(5/(10+5+2)\)、「どうのつるぎ」があたる確率は \(2/(10+5+2)\) のように考えること。
%reset -f
item_names_str = 'ひのきのぼう,こんぼう,どうのつるぎ,せいなるナイフ,くさりがま,とげのむち,まどうしのつえ,てつのやり,どくばり,てつのつめ,はがねのつるぎ,てつのオノ,あまぐものつえ,いかづちのつえ,さざなみのつえ,さばきのつえ,おおばさみ,ゆうわくのけん,りりょくのつえ,おおかなづち,はやぶさのけん,ゾンビキラー,ドラゴンキラー,くさなぎのけん,ガイアのつるぎ,ふぶきのつるぎ,いなづまのけん,まじんのオノ,らいじんのけん,もろはのつるぎ,はかいのつるぎ,おうごんのつめ,おうじゃのけん'
item_p_str = '50,50,50,40,40,35,30,30,25,20,20,20,10,10,10,10,10,10,10,10,5,5,5,4,3,3,2,2,2,2,2,2,1'上記の設定に基づき100連ガチャのシミュレーションを実行し、次のような出力を得たい。
100連ガチャの結果、以下のアイテムを獲得しました。
ひのきのぼう ... 8個
こんぼう ... 8個
どうのつるぎ ... 9個
せいなるナイフ ... 8個
くさりがま ... 11個
とげのむち ... 7個
まどうしのつえ ... 5個
てつのやり ... 7個
どくばり ... 6個
てつのつめ ... 5個
はがねのつるぎ ... 4個
てつのオノ ... 3個
あまぐものつえ ... 2個
いかづちのつえ ... 1個
さざなみのつえ ... 6個
さばきのつえ ... 2個
ゆうわくのけん ... 3個
りりょくのつえ ... 2個
おおかなづち ... 1個
ゾンビキラー ... 1個
まじんのオノ ... 1個
この要求を満たすことができるPythonプログラムを考え、実装せよ。
10.2 解答例①
%reset -f
%reset -f
import random as r
item_names_str = 'ひのきのぼう,こんぼう,どうのつるぎ,せいなるナイフ,くさりがま,とげのむち,まどうしのつえ,てつのやり,どくばり,てつのつめ,はがねのつるぎ,てつのオノ,あまぐものつえ,いかづちのつえ,さざなみのつえ,さばきのつえ,おおばさみ,ゆうわくのけん,りりょくのつえ,おおかなづち,はやぶさのけん,ゾンビキラー,ドラゴンキラー,くさなぎのけん,ガイアのつるぎ,ふぶきのつるぎ,いなづまのけん,まじんのオノ,らいじんのけん,もろはのつるぎ,はかいのつるぎ,おうごんのつめ,おうじゃのけん'
item_p_str = '50,50,50,40,40,35,30,30,25,20,20,20,10,10,10,10,10,10,10,10,5,5,5,4,3,3,2,2,2,2,2,1,1'
item_names = list(item_names_str.split(',')) # 文字列→リスト
item_p = list(map(int, item_p_str.split(','))) # 文字列→リスト
assert len(item_names) == len(item_p) # 2つのリストの長さが同じであることを確認
item_counts = [0]*len(item_names) # 結果を格納する0で初期化したリスト
n = 100
for t in range(100):
item = r.choices(item_names,weights=item_p,k=1)[0]
index = item_names.index(item)
item_counts[index]+=1
print(f'{n}連ガチャの結果、以下のアイテムを獲得しました。')
for i in range(len(item_names)):
if item_counts[i] != 0: # 1個以上獲得していれば
print(f'{item_names[i]} ... {item_counts[i]}個')10.3 解答例②
%reset -f
import random as r
item_names_str = 'ひのきのぼう,こんぼう,どうのつるぎ,せいなるナイフ,くさりがま,とげのむち,まどうしのつえ,てつのやり,どくばり,てつのつめ,はがねのつるぎ,てつのオノ,あまぐものつえ,いかづちのつえ,さざなみのつえ,さばきのつえ,おおばさみ,ゆうわくのけん,りりょくのつえ,おおかなづち,はやぶさのけん,ゾンビキラー,ドラゴンキラー,くさなぎのけん,ガイアのつるぎ,ふぶきのつるぎ,いなづまのけん,まじんのオノ,らいじんのけん,もろはのつるぎ,はかいのつるぎ,おうごんのつめ,おうじゃのけん'
item_p_str = '50,50,50,40,40,35,30,30,25,20,20,20,10,10,10,10,10,10,10,10,5,5,5,4,3,3,2,2,2,2,2,1,1'
item_names = list(item_names_str.split(',')) # 文字列→リスト
item_p = list(map(int, item_p_str.split(','))) # 文字列→リスト
assert len(item_names) == len(item_p) # 2つのリストの長さが同じであることを確認
items = r.choices(item_names,weights=item_p,k=100)
for item in set(items):
print(f'{item} ... {items.count(item)}個')