第3講 試行錯誤法でヒント数0数独の解答を作る(2)
第1話 n次魔方陣の生成を2次元配列で実現する
を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)です。
さて、いよいよ目的の数独解答自動生成アプリの開発に入りましょう。
これも段階を踏んでいきます。
ここでも新しい名称を導入します。
数字の重複を認めないという条件について
①行だけ満たすものを疑似疑似数独
(本当は疑似疑似疑似数独ですね。
解が複数ある時点で疑似がつきますから。
便宜上最初の疑似を省略します。)
②列も満たすものを疑似数独
とします。
まず、疑似疑似数独の開発をします。
ただし、疑似疑似数独は
(一見おかしそうに見えますが、一番下の行を見ると正常であることがわかります。)
となっていまい、数独の解答数どころではありませんので、
プログラムを途中で止めないと無限ループに近くになりますので、
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
はすべて不要となります。