ポケカ確率計算入門~キュワワースタートしてほしい~
こんにちは。最近ライコに夢中のダイチです。
今回は、ポケカにおける確率計算の話をします。
「大会もないこんな時期になんでポケカばっかりしているの?」と聞かれたときに「確率の勉強をしているんだよ」と返せるように、少し頭よさそうにポケカと向き合ってみます。新しいことは何もないので気楽に読んでいってください。
題材としてロストの死活問題である「キュワワースタートの確率」を扱います。他の状況でも考え方は同じなので、本稿の話を理解して応用できた方はポケカをやっているとき堂々と胸を張って、「今確率の計算をしている」と言ってください。
時系列に沿って計算する方法
では導入として最もイメージしやすい方法で計算してみましょう。
キュワワーでスタートするということは、初手7枚のうち少なくとも1枚がキュワワーであるということです。このような「少なくとも1枚」を考えるときは「余事象」という考え方を用います。「余事象」というのは、「○○が起きる」確率を計算するときに「○○が起きない」確率を計算して1から引くことで「○○が起きなくない」確率を計算し、これが「○○が起きる」確率であるという考え方です。
今回の例では、まず7枚引いて「キュワワーを引かない」確率を計算します。これを1から引くと「キュワワーを引かなくない」確率すなわち「キュワワーを引く」確率となるわけです。
イメージをつかみやすくするために、一つずつ順番に考えていきましょう。
実際の対戦準備を考えます。デッキをシャッフルし、裏向きで置きます。ここから1枚ずつ引いて、キュワワーかどうか見ていきます。
1枚目。デッキは残り60枚。その中にキュワワー以外が56枚。引きます。キュワワーじゃない。これは56/60を引いたことになります。
2枚目。デッキは残り59枚。その中にキュワワー以外が55枚。引きます。これもキュワワーじゃない。これは55/59を引いたことになります。1枚目の結果も合わせて、56/60を引いたうえで55/59を引いているので、ここまでで
$$
2枚目までキュワワーじゃない = \frac{56}{60} * \frac{55}{59} = 0.8700
$$
3枚目。デッキは残り58枚。その中にキュワワー以外が54枚。引きます。またしてもキュワワーじゃない。これは54/58を引いたことになります。1枚目と2枚目の結果も合わせて、56/60を引いたうえで55/59を引いたうえで54/58を引いているので、
$$
2枚目までキュワワーじゃない = \frac{56}{60} * \frac{55}{59} * \frac{54}{58} = 0.8100
$$
4枚目。キュワワーじゃない。53/57。
5枚目。キュワワーじゃない。52/56。
6枚目。キュワワーじゃない。51/55。
7枚目。キュワワーじゃない。50/54。
ということで、「初手にキュワワーを引かない」確率は
$$
初手にキュワワーがない = \frac{56}{60} * \frac{55}{59} * \frac{54}{58} * \frac{53}{57} * \frac{52}{56} * \frac{51}{55} * \frac{50}{54} = 0.6005
$$
対戦相手が「うわー」と言いながらカードを1枚ずつ引いているとき、このように確率が変動しているというわけです。そうしてひとしきり感情の起伏を見せた後、「なんてね」と言いながらバトル場のキュワワーを見せてくる確率が
$$
初手にキュワワーがある = 1 - 初手にキュワワーがない = 0.3999
$$
ということです。約4割ですね。
ポケカではもう一つ考慮するべき現象があります。
「うわああ」といい終えた相手が手札を全て表にして見せてきました。マリガンですね。たねポケモンがいないことを確認します。
さて、これはどれぐらいの確率で起きるのでしょうか。計算してみます。
キュワワー以外のたねポケモンが8枚、すなわち計12枚のたねポケモンを採用しているデッキを考えます。マリガンが発生する確率は、「たねポケモンを引かない」確率です。先ほどと同様に考えて、
$$
たねポケモンを引かない = \prod_{n=0}^{6} \frac{48-n}{60-n} = 0.1906
$$
約2割でマリガンをするわけです。
よって、7枚引いたときに、「キュワワー」、「キュワワー以外」、「マリガン」の3パターンに分かれるのですが、それぞれの確率は4割、4割、2割となります。
ではマリガンを終えて、最終的にキュワワーがバトル場にいる確率はどうなっているでしょうか。
マリガン後の状況を考えます。手札をデッキに戻してシャッフルし、裏向きにおいて7枚引き直します。これは対戦準備前と同じ状態ですね。この後また「キュワワー」、「キュワワー以外」、「マリガン」の3パターンに分かれるのことになりますが、その確率はそれぞれ4割、4割、2割でマリガン前と変わらないということです。よって、1回マリガンしたあとキュワワースタートする確率は
$$
1回マリガン後にキュワワースタート \\= マリガンする * 初手にキュワワーがある \\= 0.1906 * 0.3999
$$
となるわけです。これは「条件付確率」といい、何かが起きたうえで何かが起きる確率はそれぞれの確率の掛け算で表すことができます。例えばマリガンが2回連続で起きる確率は
$$
2回マリガン = マリガンする * マリガンする \\= 0.1906 * 0.1906 = 0.1906^2
$$
となります。同様に、n回マリガンする確率は
$$
n回マリガン = 0.1906^n
$$
となります。
以上を踏まえて最終的にキュワワースタートする確率を計算しましょう。
独立な現象のどれかが起きる確率は足し算で表すことができるので、
$$
キュワワースタート \\= マリガンなしでキュワワースタート + 1回マリガンしてキュワワースタート + 2回マリガンしてキュワワースタート + \cdots \\ = \sum_{n=0}^{\infin} 0.1906^n * 0.3999 \\= 0.4940
$$
約5割と計算できました。
よって、たねポケモン12枚のデッキでキュワワースタートしない確率も5割ということなので、一生キュワワースタートしないと嘆いている方がおられましたら、どれだけ不運か確率計算してみるとよいかと思います。これも条件付確率です。
組み合わせで計算する方法
先ほどの計算、いかがでしたか。少し大変ですね。今度は同じ計算を組み合わせを用いて行ってみます。
考え方は単純です。全パターン考えて、そのうちキュワワースタートするパターンがいくつか数えます。
先ほどと同じ余事象の考えを使って、初手にキュワワーがある確率を求めます。キュワワー以外の56枚から7枚引く組み合わせを考えて、
$$
初手にキュワワーがある = 1 - \frac{{}_{56} \mathrm{C}_7}{{}_{60} \mathrm{C}_7}
$$
マリガンの確率もたねポケモン以外の48枚から7枚引く組み合わせを考えて、
$$
マリガンする = \frac{{}_{48} \mathrm{C}_7}{{}_{60} \mathrm{C}_7}
$$
逆にマリガンしない確率は、
$$
マリガンしない = 1 - \frac{{}_{48} \mathrm{C}_7}{{}_{60} \mathrm{C}_7}
$$
キュワワースタートするということは、初手にたねポケモンがあるパターンのうち、キュワワーがあるパターンを考えて、
$$
キュワワースタートする \\ = \frac{初手にキュワワーがある}{マリガンしない} \\= \frac{1 - \frac{{}_{56} \mathrm{C}_7}{{}_{60} \mathrm{C}_7}}{1 - \frac{{}_{48} \mathrm{C}_7}{{}_{60} \mathrm{C}_7}} \\ = 0.4936
$$
よって約5割となります。
少しすっきりして見えたでしょうか。
組み合わせの計算が大変なため、実は計算量そのものはあまり変わっていません。手計算は大変ということです。
試して計算する方法
ポケカの確率計算では数値の厳密さは重要ではありません。キュワワースタートの確率は約5割であり、それが実際は4割9分だとしても困ることはありません。
ということで、最後は数学のいらない方法です。
実際にゲームの準備をして、何回やって何回キュワワーでスタートしたかを「キュワワースタートの確率」とします。
10回やって3回成功したら3割という具合です。
かなり直感的で、特に意識せずに使っている方も多いのではないでしょうか。勝率の計算もこの考え方ですね。
では、やってみましょう。
試しに1000回やってみます。
ちょっと面倒なのでコンピュータにやってもらいます。
初手を見て、キュワワーがいたらその回数をカウントしていくだけです。
import numpy as np
# デッキ情報
deck_size = 60
hand_size = 7
total_basic_pokemon = 12
comfey_num = 4
# 実行回数
num_simulations = 1000
def simulate_hand(deck, hand_size):
return np.random.choice(deck, hand_size, replace=False)
def contains_comfey(hand):
return np.sum(hand == 'comfey') > 0
def contains_basic_pokemon(hand):
return np.sum((hand == 'basic') | (hand == 'comfey')) > 0
# デッキを準備
deck = np.array(['comfey'] * comfey_num + ['basic'] * (total_basic_pokemon - comfey_num) + ['other'] * (deck_size - total_basic_pokemon))
# キュワワースタート回数を計算
total_success_count = []
trials = []
success_count = 0
for i in range(num_simulations):
hand = simulate_hand(deck, hand_size)
has_basic = contains_basic_pokemon(hand)
has_comfey = contains_comfey(hand)
if has_basic:
if has_comfey:
success_count += 1
else:
while not contains_basic_pokemon(hand):
hand = simulate_hand(deck, hand_size)
if contains_comfey(hand):
success_count += 1
total_success_count.append(success_count)
trials.append(i + 1)
# 確率を計算
probabilities = [success / trials for success, trials in zip(total_success_count, trials)]
#表示
final_estimated_probability = probabilities[-1]
print(final_estimated_probability)
0.463と出ました。
この1000回の試行中に確率がどう変化したか見てみます。
0.4と0.5の間になりそう、という感じですね。試行回数をもっと増やしてみましょう。
0.49とかその辺りでしょうか。20000回目以降はほとんど変化が見られないですね。
ここで問題になるのが、「何回やれば十分なのか」です。
これを見ると100回では足りなさそうです。でも何万回もする必要はなさそうですよね。
今日はあまり深い議論をするつもりはないので、適当に言葉を濁しておきます。
「実際のデッキで試せる回数(数十〜数百回)程度では、やればやるほど精度は上がる」
ということでたくさん試しましょう。
このように試して確率を求める方法を「モンテカルロ法」と呼んだりするのですが、普段のデッキの調整はこれをやっていることになります。
ということで、本稿の結論を述べて終わります。
ポケカの対戦中にその必要性について問われたらこう言いましょう。
「今モンテカルロ法で計算をしているんだ。試行回数が大事なんだよ。わかるだろ」と。