■ 最新の投稿

VBAパズル解析~覆面算に挑戦『BASE+BALL=GAMES⚾』

『覆面算』とは、記号で表された数式の空欄に、0~9の数字を当てはめて完成させるパズルです。推理しながら正しい計算式を導きます。

大事なルール:違うアルファベットには違う数字、同じアルファベットには同じ数字を入れる。

「BASE + BALL = GAMES」は、7種類のアルファベット {A, B, E, G, L, M, S} で構成された文字です。これらのアルファベット に、便宜上インデックス(番号)を割り振ります。ただし、ここで付ける番号はアルファベット に対応する数値ではなく、識別のためのインデックスです。混同を避けるため、インデックスは括弧書きで表記します。

こういうことです。

次の等式が成立するものを力技で探します。

                 (2)×1000 + (1)×100 + (7)×10 + (3)×10
+                (2)×1000 + (1)×100 + (5)×10 + (5)×10
=  (4)×10000 + (1)×1000 + (6)×100 + (3)×10 + (7)×10

① VBAのModule1に次のコードを記載します。

Option Explicit

Public Sub BaseBallGames()

    ' タイトル
    Const TITLE As String = "Base Ball Games"

    ' 単純なループカウンタ
    Dim lp1 As Long, lp2 As Long
    
    Const P As Long = 10     ' 要素数
    Const N As Long = 7      ' 選択できる数

    ' 答えの数をカウントする変数
    Dim ansCnt As Long

    ' パズルの解析を始めるか問い合わせる。
    Dim ret
    ret = MsgBox( _
                      "パズルの解析を始めますか?" _
                    , vbYesNo + vbQuestion _
                    , TITLE _
                )

    ' [No]の場合は処理を継続しない。
    If ret = vbNo Then
        Exit Sub
    End If

    ' 1~10の順列を作成する。
    Dim permArray() As Integer
    permArray = makePermutation(P, N)

    ' 連想配列を作成する。
    Dim hs As Object
    Set hs = CreateObject("Scripting.Dictionary")
    hs.Add 1, 1
    hs.Add 2, 2
    hs.Add 3, 3
    hs.Add 4, 4
    hs.Add 5, 5
    hs.Add 6, 6
    hs.Add 7, 7
    hs.Add 8, 8
    hs.Add 9, 9
    hs.Add 10, 0  ' ※10→0

    ' 作成した順列の内容を書き換える。
    For lp1 = 1 To UBound(permArray, 1)
        For lp2 = 1 To UBound(permArray, 2)
    
            ' 連想配列を使って書き換える。
            permArray(lp1, lp2) _
                = hs.Item(permArray(lp1, lp2))
        
        Next lp2
    Next lp1

    ' 作った配列を一つずつ検証する。
    For lp1 = 1 To UBound(permArray, 1)
        
        ' 作業用配列を作る。
        Dim wk() As Long
        ReDim wk(UBound(permArray, 2)) As Long
        
        For lp2 = 1 To UBound(permArray, 2)
            wk(lp2) = permArray(lp1, lp2)
        Next lp2
        
        ' "BASE"を作る。
        Dim base As Long
        base = wk(2) * 1000 _
            + wk(1) * 100 _
            + wk(7) * 10 _
            + wk(3)
        
        ' "BALL"を作る。
        Dim ball As Long
        ball = wk(2) * 1000 _
            + wk(1) * 100 _
            + wk(5) * 10 _
            + wk(5)
        
        ' "GAMES"を作る。
        Dim games As Long
        games = wk(4) * 10000 _
            + wk(1) * 1000 _
            + wk(6) * 100 _
            + wk(3) * 10 _
            + wk(7)
        
        ' 条件Aを検証する。
        If base + ball = games _
            And wk(2) > 0 _
            And wk(4) > 0 Then

            ' 見つかった答えを書き出す。
            With Range("A1")
                .Cells(ansCnt * 4 + 1, 3).Value = wk(2)
                .Cells(ansCnt * 4 + 1, 4).Value = wk(1)
                .Cells(ansCnt * 4 + 1, 5).Value = wk(7)
                .Cells(ansCnt * 4 + 1, 6).Value = wk(3)
                .Cells(ansCnt * 4 + 2, 1).Value = "+"
                .Cells(ansCnt * 4 + 2, 3).Value = wk(2)
                .Cells(ansCnt * 4 + 2, 4).Value = wk(1)
                .Cells(ansCnt * 4 + 2, 5).Value = wk(5)
                .Cells(ansCnt * 4 + 2, 6).Value = wk(5)
                .Cells(ansCnt * 4 + 3, 2).Value = wk(4)
                .Cells(ansCnt * 4 + 3, 3).Value = wk(1)
                .Cells(ansCnt * 4 + 3, 4).Value = wk(6)
                .Cells(ansCnt * 4 + 3, 5).Value = wk(3)
                .Cells(ansCnt * 4 + 3, 6).Value = wk(7)
            End With

            ' 枠線の描画を行う。
            Range("A1:F1").Offset(ansCnt * 4 + 1, 0) _
                .Borders(xlEdgeBottom).LineStyle = xlContinuous

            ' 答えの数をカウントする。
            ansCnt = ansCnt + 1
        
        End If
    
        ' メモリを解放する。
        Erase wk
    
    Next lp1

    ' 答えが見つかったかどうか判別する。
    If ansCnt = 0 Then
        
        MsgBox "答えは見つかりませんでした。" _
                , vbExclamation _
                , TITLE

    Else
        
        MsgBox "答えは" & ansCnt & "件見つかりました。" _
                , vbInformation _
                , TITLE

    End If

    ' メモリを解放する。
    Erase permArray

    ' オブジェクトを解放する。
    Set hs = Nothing

End Sub

Public Function makePermutation( _
      N As Long _
    , r As Long _
) As Integer()

    ' 単純なループカウンタ
    Dim lp1 As Long, lp2  As Long, lp3 As Long, lp4 As Long
    
    ' 順列数特定用変数
    Dim caseCnt As Long
    
    ' 戻り値作成用変数
    Dim ret() As Integer

    ' 順列数を特定する。
    caseCnt = 1
    For lp1 = N To (N - r + 1) Step -1
        caseCnt = caseCnt * lp1
    Next lp1
    
    ' 戻り値配列を作成する。
    ReDim ret(caseCnt, r) As Integer

    ' 作成済みの順列の数を保持する変数
    Dim pos As Long
    pos = 1

    ' 作成中の順列の数を保持する変数
    Dim wkPos As Long
    wkPos = 1

    ' 順列の最初の一つを作成する。
    For lp1 = 1 To r
        ret(pos, lp1) = lp1
    Next lp1

    ' 二番目に大きな値から順に処理する。
    For lp1 = N - 1 To 1 Step -1
    
        ' "一つ大きな値"~"一番大きな値"まで順に処理する。
        For lp2 = lp1 + 1 To N
        
            ' 作成済みの順列を一つずつ処理する。
            For lp3 = 1 To pos
            
                ' 作成中の順列の数を一つ増やす。
                wkPos = wkPos + 1
                
                ' 一度でも入れ替え処理を行ったか判別するための
                ' フラグをFalseで初期化する。
                Dim flg As Boolean
                flg = False
                
                ' lp2とlp1が指す値の入れ替え処理を行う。
                For lp4 = 1 To r
                
                    If ret(lp3, lp4) = lp1 Then
                    
                        ret(wkPos, lp4) = lp2
                        flg = True
                    
                    ElseIf ret(lp3, lp4) = lp2 Then
                        
                        ret(wkPos, lp4) = lp1
                        flg = True
                    
                    Else
                    
                        ret(wkPos, lp4) = ret(lp3, lp4)
                    
                    End If
                    
                Next lp4
            
                ' 一度でも入れ替え処理を行ったか判別する。
                If flg = False Then
                
                    ' 今作った順列を削除する。
                    wkPos = wkPos - 1
                
                End If
                
            Next lp3
        
        Next lp2
        
        ' 作成済みの順列の数を更新する。
        pos = wkPos
        
    Next lp1
                
    ' 作成した順列を呼び出し元に返す。
    makePermutation = ret
    
End Function

② Sheet1を開いた状態でBaseBallGames() を動かしてみましょう。

見つかった答えは1つだけでした。

回答はこれです!


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です