はじめに
先に ブラックジャックの親のバーストの確率を乱数を使用せず、確率計算のみでプログラムで計算する という記事を投稿しましたが、別のアプローチ(アルゴリズム)で同様の確率を計算し、アルゴリズムやロジックに不具合がないかということを同じ結果になるかどうかで検証してみました。
プログラム
検証用ツール部分
セル関数で検証可能にします。
'*****************************************************************************
'[概要] 親のハンドの確率をシュミレーションし配列数式で返す
'[引数] LoopCnt:ループ回数
' Decks:トランプの組数
'[戻値] 配列数式
'*****************************************************************************
Public Function SimulateHandRate(ByVal LoopCnt As Long, ByVal Decks As Double) As Variant
Dim Result(1 To 10, 17 To 22) As Long
Call SetHands(LoopCnt, Decks, Result())
SimulateHandRate = Result
End Function
以下のC4セルのように =SimulateHandRate(Loop回数,組数)
でセル関数を入力します。
その時、配列数式となるように、10行6列(例では、C4:H13)の範囲を選択し、Ctrl+Shift+Enter を押下します。
計算メイン部分
Option Explicit
Private Cards() As Long '山のカードの配列
Private CardPoint As Long '山の何枚目のカードか
'*****************************************************************************
'[概要] LOOP回数試行した各ハンドの出現回数を設定する
'[引数] LoopCnt:ループ回数
' Decks:トランプの組数
' Result:各ハンドの出現回数
'*****************************************************************************
Private Sub SetHands(ByVal LoopCnt As Long, ByVal Decks As Double, ByRef Result() As Long)
Call Initilize(Decks)
Call Shuffle
Dim SaveCardPoint As Long
Dim i As Long
Dim Hand As Long
For i = 1 To LoopCnt
'カードの山を4分の3まで使用するとシャッフルする
If CardPoint >= UBound(Cards) * 0.75 Then
Call Shuffle
End If
SaveCardPoint = CardPoint
Dim OpenCard As Long
OpenCard = Hit()
Hand = Deal(OpenCard)
Result(OpenCard, Hand) = Result(OpenCard, Hand) + 1
'10や9は次のカードをHitする確率が低く、2や3は高くなるので
'カードの出現確率の偏りを是正させる
'詳しくは記事の後半を参照ください。
If SaveCardPoint + 5 > CardPoint Then
CardPoint = SaveCardPoint + 5
End If
Next
End Sub
'*****************************************************************************
'[概要] カードの山を作成する
'[引数] Decks:トランプの組数
'*****************************************************************************
Private Sub Initilize(ByVal Decks As Double)
ReDim Cards(1 To Decks * 52) As Long
Dim i As Long
Dim j As Long
Dim k As Long
For i = 1 To Decks * 4
For j = 1 To 13
k = k + 1
Cards(k) = j
Next
Next
End Sub
'*****************************************************************************
'[概要] カードをシャッフルする
'[引数] なし
'*****************************************************************************
Private Sub Shuffle()
CardPoint = 0
Dim i As Long
Dim j As Long
Dim Swap As Long
For i = 1 To UBound(Cards) - 1
j = RandBetween(i, UBound(Cards))
Swap = Cards(i)
Cards(i) = Cards(j)
Cards(j) = Swap
Next
End Sub
'*****************************************************************************
'[概要] 最小値と最大値の間の乱数を発生させる
'[引数] 最小値と最大値
'[戻値] 乱数
'*****************************************************************************
Private Function RandBetween(ByVal min As Long, ByVal max As Long) As Long
RandBetween = Int(Rnd() * (max - min + 1)) + min
End Function
'*****************************************************************************
'[概要] 17以上になるまでカードを引く
'[引数] OpenCard:オープンカード
'[戻値] 17~22のいずれか(22はバースト)
'*****************************************************************************
Private Function Deal(ByVal OpenCard As Long) As Long
Dim Hand As Long
Dim Card As Long
Dim IsSoftHand As Boolean
If OpenCard = 1 Then
IsSoftHand = True
Hand = 11
Else
IsSoftHand = False
Hand = OpenCard
End If
Do While (Hand < 17)
Card = Hit()
Hand = Hand + Card
If Card = 1 And IsSoftHand = False Then
IsSoftHand = True
Hand = Hand + 10
End If
If Hand > 21 And IsSoftHand = True Then
IsSoftHand = False
Hand = Hand - 10
End If
Loop
If Hand > 21 Then
Deal = 22
Else
Deal = Hand
End If
End Function
'*****************************************************************************
'[概要] カードを1枚引く
'[引数] なし
'[戻値] 引いたカード
'*****************************************************************************
Private Function Hit() As Long
CardPoint = CardPoint + 1
Hit = Cards(CardPoint)
If Hit > 10 Then
Hit = 10
End If
End Function
やっていること
1組52枚(関数の引数によって指定可能)のカードをシャッフルし、上から順番に17以上になるまで引くことを繰り返しながら、初手のカードごとの最終手札のバリエーションをひたすらカウントする。
ただし、カードの山が残り4分の1より少なくなったら、再度カードをシャッフルする。
計算結果(1組52枚の場合)
先の記事(理論計算値) の計算結果
初手 | 17 | 18 | 19 | 20 | 21 | BS |
---|---|---|---|---|---|---|
1 | 12.61% | 13.10% | 12.95% | 13.16% | 36.53% | 11.65% |
2 | 13.90% | 13.18% | 13.18% | 12.39% | 12.05% | 35.30% |
3 | 13.03% | 13.09% | 12.38% | 12.33% | 11.60% | 37.56% |
4 | 13.10% | 11.42% | 12.07% | 11.63% | 11.51% | 40.28% |
5 | 11.97% | 12.35% | 11.69% | 10.47% | 10.63% | 42.89% |
6 | 16.69% | 10.65% | 10.72% | 10.07% | 9.79% | 42.08% |
7 | 37.23% | 13.86% | 7.73% | 7.89% | 7.30% | 25.99% |
8 | 13.09% | 36.30% | 12.94% | 6.83% | 6.98% | 23.86% |
9 | 12.19% | 10.39% | 35.74% | 12.23% | 6.11% | 23.34% |
10 | 11.44% | 11.29% | 11.47% | 32.89% | 11.49% | 21.43% |
平均 | 14.58% | 13.81% | 13.48% | 17.58% | 12.19% | 28.36% |
今回 1億3000万回繰返した結果
実行結果
初手 | 17 | 18 | 19 | 20 | 21 | BS | 合計 |
---|---|---|---|---|---|---|---|
1 | 126380 | 131260 | 129048 | 131212 | 365569 | 116853 | 1000322 |
2 | 139138 | 131818 | 131444 | 124460 | 120162 | 353348 | 1000370 |
3 | 130192 | 131530 | 123751 | 122759 | 115564 | 376013 | 999809 |
4 | 130898 | 114257 | 120800 | 116576 | 115243 | 403272 | 1001046 |
5 | 119684 | 122523 | 117001 | 104278 | 106330 | 429089 | 998905 |
6 | 166675 | 106440 | 107292 | 100681 | 98478 | 420882 | 1000448 |
7 | 372843 | 138616 | 77087 | 79167 | 72838 | 259714 | 1000265 |
8 | 130869 | 364078 | 129595 | 68339 | 69911 | 238035 | 1000827 |
9 | 121949 | 103724 | 357026 | 122436 | 61308 | 233736 | 1000179 |
10 | 456808 | 450507 | 458406 | 1315549 | 459177 | 857382 | 3997829 |
合計 | 1895436 | 1794753 | 1751450 | 2285457 | 1584580 | 3688324 | 13000000 |
実行結果から計算した確率
初手 | 17 | 18 | 19 | 20 | 21 | BS |
---|---|---|---|---|---|---|
1 | 12.63% | 13.12% | 12.90% | 13.12% | 36.55% | 11.68% |
2 | 13.91% | 13.18% | 13.14% | 12.44% | 12.01% | 35.32% |
3 | 13.02% | 13.16% | 12.38% | 12.28% | 11.56% | 37.61% |
4 | 13.08% | 11.41% | 12.07% | 11.65% | 11.51% | 40.29% |
5 | 11.98% | 12.27% | 11.71% | 10.44% | 10.64% | 42.96% |
6 | 16.66% | 10.64% | 10.72% | 10.06% | 9.84% | 42.07% |
7 | 37.27% | 13.86% | 7.71% | 7.91% | 7.28% | 25.96% |
8 | 13.08% | 36.38% | 12.95% | 6.83% | 6.99% | 23.78% |
9 | 12.19% | 10.37% | 35.70% | 12.24% | 6.13% | 23.37% |
10 | 11.43% | 11.27% | 11.47% | 32.91% | 11.49% | 21.45% |
平均 | 14.58% | 13.81% | 13.47% | 17.58% | 12.19% | 28.37% |
先の記事(理論計算値) からの乖離幅
初手 | 17 | 18 | 19 | 20 | 21 | BS |
---|---|---|---|---|---|---|
1 | 0.02% | 0.02% | -0.05% | -0.04% | 0.02% | 0.03% |
2 | 0.01% | 0.00% | -0.04% | 0.05% | -0.04% | 0.02% |
3 | -0.01% | 0.06% | 0.00% | -0.06% | -0.05% | 0.05% |
4 | -0.02% | 0.00% | 0.00% | 0.02% | 0.00% | 0.00% |
5 | 0.01% | -0.08% | 0.02% | -0.03% | 0.01% | 0.07% |
6 | -0.03% | -0.01% | 0.01% | -0.01% | 0.06% | -0.01% |
7 | 0.04% | 0.00% | -0.03% | 0.02% | -0.02% | -0.02% |
8 | -0.01% | 0.08% | 0.00% | 0.00% | 0.01% | -0.08% |
9 | 0.00% | -0.02% | -0.04% | 0.02% | 0.02% | 0.03% |
10 | -0.02% | -0.02% | 0.00% | 0.02% | 0.00% | 0.02% |
平均 | 0.00% | 0.00% | -0.01% | 0.00% | 0.00% | 0.01% |
結論
先に理論値で計算した結果とほとんど一致したので、アルゴリズムやロジックに不具合は、ほぼないと言えると思います。
カードの引き方による理論値との乖離について
以下の部分を削除して実行した結果
'10や9は次のカードをHitする確率が低く、2や3は高くなるので
'カードの出現確率の偏りを是正させる
'詳しくは記事の後半を参照ください。
'If SaveCardPoint + 5 > CardPoint Then
' CardPoint = SaveCardPoint + 5
'End If
実行結果 の合計列のオレンジで示したセルの初手の出現数が少なめにカウントされている傾向が見て取れると思います。
また、理論計算値からの乖離幅 の表の黄色で示したセルの確率が、理論計算値よりも低くなる傾向が見て取れると思います。
このことから、10や9は次のカードをHitする確率が低くなることによりカードの出現確率が低めに偏り、このような傾向を生み出したと考えられると思います。
しかし、どちらかと言えばこちらの確率の方がより実戦時の確率に近いのではないかとも思われます。
関連記事
こちらの記事も併せてお読みください。
ブラックジャックの親のバーストの確率を乱数を使用せず、確率計算のみでプログラムで計算する
ブラックジャックの親のバーストの確率をノーコードで計算する
ブラックジャックの期待値をノーコードで計算する