第3講 試行錯誤法でヒント数0数独の解答を作る(2)
第1話 n次魔方陣の生成を2次元配列で実現する

002
を2次元配列で実現させるプログラム例

Dim n As Byte, m(3, 3) As Byte, cn As Integer 'nは魔方陣の次数、x(15)は魔方陣を収納する配列
Private Sub CommandButton1_Click()
  CommandButton2_Click
  n = Cells(3, 2)
  cn = 0
  f (0) 'n次魔方陣作成プロシージャ
End Sub
Sub f(g As Byte)
  Dim i As Byte, j As Byte, k As Byte, h As Byte, a As Integer, s As Integer, w As Byte
  Dim y As Byte, x As Byte
  
y = Int(g / n) 'セル番号をnで割った商でy(縦)座標を取得
  x = g Mod n 'セル番号をnで割った余りでx(横)座標を取得

  a = cn Mod 10
  s = Int(cn / 10) '以上の2行は生成された魔方陣を適切な位置に表示するためのもの。
  For i = 0 To n * n - 1
    m(y, x) = i + 1
    h = 1
    
If g > 0 Then '過去との重複を検査して重複がある場合にはhを0として以下の処理を行わせない。
      For j = 0 To g - 1
        If m(y, x) = m(Int(j / n), (j Mod n)) Then
          h = 0
          Exit For
        End If
      Next
    End If
    If h = 1 And x = n - 1 Then '横合計検査、合計が1からn*nの合計÷nに等しくない場合にはhを0として以下の処理を行わせない。
      w = 0
      For j = 0 To n - 1
        w = w + m(y, j)
      Next
      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 Then '縦合計検査、合計が1からn*nの合計÷nに等しくない場合にはhを0として以下の処理を行わせない。
      w = 0
      For j = 0 To n - 1
        w = w + m(j, x)
      Next
      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 And x = 0 Then '対角線合計検査、合計が1からn*nの合計÷nに等しくない場合にはhを0として以下の処理を行わせない。
      w = 0
      For j = 0 To n - 1
        w = w + m(n - 1 - j, j)
      Next
      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 And x = n - 1 Then '逆対角線合計検査、合計が1からn*nの合計÷nに等しくない場合にはhを0として以下の処理を行わせない。
      w = 0
      For j = 0 To n - 1
        w = w + m(j, j)
      Next
      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If

    If h = 1 Then '重複検査・横合計検査・縦合計検査・対角線合計検査をパスした場合に次のセル(箱)番号の世界に飛ぶ。
      If g + 1 < n * n Then 'ただし、セル(箱)番号がn * n - 1以下のとき
        f (g + 1)
      Else 'セル(箱)番号がn * n - 1のとき、疑似魔方陣が完成しているので、表示して疑似疑似魔方陣総数をカウント
        
For j = 0 To n * n - 1
          Cells(4 + Int(j / n) + (n + 1) * s, 1 + (j Mod n) + (n + 1) * a) = m(Int(j / n), (j Mod n))
        Next

        cn = cn + 1
      End If
    End If
  Next
End Sub
Private Sub CommandButton2_Click()
  Rows("4:200").Select
  Selection.ClearContents
  Cells(1, 1).Select
End Sub

参考ダウンロード添付ファイル
※普通座標は(横座標,縦座標)ですが、2次元配列配列が(縦,横)となっていますので、
これに合わせて座標を(縦座標,横座標)としていることに注意して下さい。
すなわち、(x,y)ではなく(y,x)です。


さて、いよいよ目的の数独解答自動生成アプリの開発に入りましょう。
これも段階を踏んでいきます。
ここでも新しい名称を導入します。
数字の重複を認めないという条件について
①行だけ満たすものを疑似疑似数独
(本当は疑似疑似疑似数独ですね。
解が複数ある時点で疑似がつきますから。
便宜上最初の疑似を省略します。)
②列も満たすものを疑似数独
とします。
まず、疑似疑似数独の開発をします。
ただし、疑似疑似数独は
002
(一見おかしそうに見えますが、一番下の行を見ると正常であることがわかります。)
となっていまい、数独の解答数どころではありませんので、
プログラムを途中で止めないと無限ループに近くになりますので、
10個出来た段階で止めることにしましょう。
それには、
        If cn = 10 Then Exit Sub
という文を適切な場所に入れなければなりません。

今回もヒントを入れておきます。
  For i = 0 To n * n - 1
は変更が必要になります。
重複検査の部分
    If g > 0 Then
      For j = 0 To g - 1
        If m(y, x) = m(Int(j / n), (j Mod n)) Then
          h = 0
          Exit For
        End If
      Next
    End If
も改変が必要となります。
現状ではすべての過去との重複の検査をしていますが、
疑似疑似数独では行のみの重複検査に限定されます。
それから、合計検査の部分
    If h = 1 And x = n - 1 Then
      w = 0
      For j = 0 To n - 1
        w = w + m(y, j)
      Next

      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 Then
      w = 0
      For j = 0 To n - 1
        w = w + m(j, x)
      Next

      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 And x = 0 Then
      w = 0
      For j = 0 To n - 1
        w = w + m(n - 1 - j, j)
      Next

      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
    If h = 1 And y = n - 1 And x = n - 1 Then
      w = 0
      For j = 0 To n - 1
        w = w + m(j, j)
      Next

      If w <> Int(n * (n * n + 1) / 2) Then
        h = 0
      End If
    End If
はすべて不要となります。




第2講第10話へ 第2話へ


トップへ