u++の備忘録

「一発ジャンケン」の確率シミュレーション

Yahoo!ニュースを眺めていて見かけた「一発ジャンケン」が、プログラミングの教材として良さげだったので、実際に簡単なプログラムを組んでみました。ルールは以下の通りです(記事からの抜粋)。

まず、1~5を指で出す。数字が大きい人が勝ちなので5が一番強いが、他の人と同じ数字を出した人は負けとなる。5は強いが被りやすいという難点がある。ただ、残りが5と1なら革命で1が勝ちとなる。

news.yahoo.co.jp

import collections
import random


def determine_winner(choices):
    agg = collections.Counter(choices)
    only_one_choice = [i for i in agg.keys() if agg[i] == 1]
    if not only_one_choice:
        return 999, 999
    elif set(only_one_choice) == set([1, 5]):
        return choices.index(1), 1
    else:
        return choices.index(max(only_one_choice)), max(only_one_choice)


if __name__ == "__main__":

    LARGE_NUMBER = 100000
    N = 30

    store = []
    win_choice = []
    for n in range(5, N):
        results = [
            determine_winner([random.randint(1, 5) for _ in range(n)])
            for _ in range(LARGE_NUMBER)
        ]
        store.append(
            collections.Counter([result[0] for result in results])[999] / LARGE_NUMBER
        )
        win_choice.append(collections.Counter([result[1] for result in results]))

参加者数ごとの引き分けが発生する確率は以下の通りです。

import pandas as pd


df = pd.DataFrame({
    "The number of participants": range(5, N),
    "Draw probability": store,
})
df.index = df["The number of participants"]
df["Draw probability"].plot(xlabel="The number of participants", ylabel="Draw probability")

参加者数ごとの勝者となる手は以下の通りです。

import matplotlib.pyplot as plt


v1 = [wc[1] for wc in win_choice]
v2 = [wc[2] for wc in win_choice]
v3 = [wc[3] for wc in win_choice]
v4 = [wc[4] for wc in win_choice]
v5 = [wc[5] for wc in win_choice]
v999 = [wc[999] for wc in win_choice]
labels = ["1", "2", "3", "4", "5", "draw"]
fig, ax = plt.subplots()
ax.stackplot(range(5, N), v1, v2, v3, v4, v5, v999, labels=labels)
ax.legend(loc='upper right')
plt.show()