32. 演習6: 簡易ガチャ・シミュレータを実装してみよう。

  • 作業に関する補足

    • 今後の演習では、指定がない限りはインタプリタ・エディタ・統合環境いずれを使用しても構いません。また、関数を作れという指示がない場合でも、自由に作成OKです。

  • シミュレータの概要

    • ガチャとは一種のクジびきである。

    • ここでは「数字の付いたカード」を排出するガチャを実装しよう。全体で100種類のカードがあり、全てのカード出現率は等確率(100分の1)とする。

    • カードは、int型オブジェクトの1〜100で表現するものとする。

    • 一度のガチャで引けるカードは1枚であり、全てのカード(1〜100のint型オブジェクト)は、毎回同一確率で出現するものとする。つまり、100回引いたとしても全てのカードを得られるとは限らないものとする。

    • 最終的に作成するシミュレータは、目的のカードが得られるまで平均的に何回引けばよいのかを検証するプログラムである。


32.1. ガチャ関数 draw_card() を作成せよ

  • draw_card() は実行するたびに任意のカード1枚を返すものとする。

  • 関数仕様

    • 関数名: draw_card

    • 引数: なし

    • 戻り値: ガチャ結果(任意のカード1枚)

  • 補足

    • レポートには draw_card() のコードと、その関数を2回以上実行した様子を掲載すること。

  • 実行例

>>> # draw_card()の実行例
>>> draw_card()
25
>>> draw_card()
9

32.2. draw_card()の動作が想定通りであることを確認するユニットテストを書け

  • ユニットテストはインタプリタでは動作しないため、ex6.pyというファイルに draw_card() を定義し、その関数内でユニットテストを書こう。

  • テスト内容は「draw_card()の実行結果が、1〜100のint型オブジェクトであること(その範囲内に収まっていること)」とする。

  • 補足

    • レポートには、(1)ユニットテストが通ったことを示す実行結果を掲載すると共に、(2)この時点でのex6.pyのコードを掲載すること。


32.3. 目的のカードを引き当てるのに要した回数を数えよ

  • あなたは目的のカードを引き当てるまで永遠と draw_card() を実行する必要がある。言い換えると、目的カードが出るまでは終わることができない。ループ処理を用いて、目的のカードを引くまでにかかった回数をカウントする関数と、実行結果を示せ。

  • 補足

    • 目的のカードは任意に決めて構わない。ここでは便宜上1とする。

    • レポートには、コードと実行結果を掲載せよ。

  • 関数仕様

    • 名前: number_of_drawing

    • 引数: 目的のカード

    • 戻り値: 目的カードを引き当てるのに要した回数

    • 例えば、目的カードが1ならば「number_of_drawing(1)」として実行すると、1を引き当てるのに要した回数を返せ。

  • 実行例

>>> # number_of_drawing()の実行例
>>> number_of_drawing(1)
57
>>> number_of_drawing(1)
17

32.4. 「平均的に何回カードを引けば当たりそうか」を検証するシミュレータ(プログラム)を作成せよ。

  • number_of_drawing()は実行するたびにその結果が大きく変化するはずだ。これは「等確率で用意したガチャにおいて、狙ったカードを引き当てる」という行為の結果には大きな違いが現れることを意味する。ここでは「たまたま運が良かった」といった個々の事例についてはひとまず置いておき、結果をより一般的に解釈するために「平均的に何回カードを引けば当たりそうか」を検証したい。

  • 平均回数を求めるために、ここではnumber_of_drawing()を1000回実行し、その平均回数を求めるものとする。

  • 関数仕様

    • 関数名: drawing_simulator

    • 引数: number_of_drawing()を実行する回数(デフォルト値1000)

    • 戻り値: number_of_drawing()の平均値(float型)

  • 補足

    • レポートには、プログラムとその実行結果を掲載せよ。

  • 実行例

# 実行結果の例
>>> drawing_simulator()
98.29
>>> drawing_simulator()
104.75

32.5. シミュレータを拡張しよう

  • drawing_simulator()に以下の機能を追加せよ。

    • シミュレーション中で得られた最小回数(最も運が良かった回数)を戻り値に加えよ。

    • シミュレーション中で得られた最大回数(最も運が悪かった回数)を戻り値に加えよ。

  • 関数仕様

    • 関数名: drawing_simulator

    • 引数: number_of_drawing()を実行する回数(デフォルト値1000)

    • 戻り値: 下記3つの値

      • number_of_drawing()の平均値(float型), 最小回数(int型), 最大回数(int型)

  • 補足

    • 変わるのは戻り値だけである。関数名・引数は演習6.3と同一のままとする。

  • 実行例

>>> # 実行結果の例
>>> drawing_simulator()
(99.553, 1, 667)
>>> drawing_simulator()
(104.953, 1, 732)

32.6. 演習6.5の実行結果を元に、このガチャの特徴について考察せよ

  • 例えば上記の実行結果例では「最低1回で欲しかったクジを引き当てた人」がいる一方で「引き当てるまでに732回かかった人もいる」ことを示している。全体の平均では100回前後になるが、このような差異は望ましい結果だろうか?

  • 今回の考察に正解はありません。「等確率のガチャ」と「得られた結果」から、このガチャの特徴や良し悪し等について考察してみてください。(数行程度でok)


32.7. (余裕のあるチーム向け) 確率が異なるガチャを用意してみよう。

  • 演習6.6までは「等確率で出てくるガチャ」を想定したコードになっている。世の中には確率が異なるガチャが多々あるが、どのように実装したら良いだろうか? 自由に検討・実装してみよう。