第23講 数独を解くソフトVer.1の制作
第3話 数独を解くソフトVer.0コード解説
コード再掲
Dim m(9, 9), cn As Byte
Private Sub CommandButton1_Click()
hj = Timer
cn = 0
dainyuu
f (0)
ow = Timer
Cells(3, 22) = "作成時間は"
Cells(3, 23) = ow - hj
Cells(3, 24) = "秒です。"
End Sub
Sub dainyuu()
Dim i As Byte, j As Byte
For i = 0 To 8
For j = 0 To 8
m(i, j) = Cells(3 + i, 2 + j)
Next
Next
For i = 0 To 8
For j = 0 To 8
If m(i, j) < 1 Then
m(i, j) = 0
End If
Next
Next
End Sub
Sub f(g As Integer)
Dim x As Byte, y As Byte
Dim i, j, k, zy, zx As Byte
y = Int(g / 9)
x = g Mod 9
If m(x, y) > 0 Then
If g + 1 < 81 Then
f (g + 1)
Else
For j = 0 To 8
For k = 0 To 8
Cells(13 + j, 2 + k) = m(j, k)
Next
Next
cn = cn + 1
If cn = 1 Then Exit Sub
End If
Exit Sub
End If
Dim xa As Byte, ya As Byte, xs As Byte, ys As Byte, kk As Byte, kkk As Byte
xa = x Mod 3
ya = y Mod 3
xs = Int(x / 3)
ys = Int(y / 3)
kk = 9 * Rnd()
For i = 1 To 9
kkk = (kk + 4 * i) Mod 9 + 1
m(x, y) = kkk
For j = 0 To 8
If j <> x Then
If m(x, y) = m(j, y) Then GoTo tobi
End If
Next
For j = 0 To 8
If j <> y Then
If m(x, y) = m(x, j) Then GoTo tobi
End If
Next
For j = 0 To 2
For k = 0 To 2
If 3 * xs + j <> x And 3 * ys + k <> y Then
If m(x, y) = m(3 * xs + j, 3 * ys + k) Then GoTo tobi
End If
Next
Next
If g + 1 < 81 Then
f (g + 1)
Else
For j = 0 To 8
For k = 0 To 8
Cells(13 + j, 2 + k) = m(j, k)
Next
Next
cn = cn + 1
If cn = 1 Then Exit Sub
End If
If cn = 1 Then Exit Sub
tobi:
Next
m(x, y) = 0
End Sub
解説
hj = Timer
は現在の時刻を取得しています。
hjはただの変数です。
VBAでは、変数の宣言をしなくてもよいのです。
ただ、容量のかかるバリアント型になってしまうという問題がありますが、
時刻の取得だけなら別に問題ありません。
hjを使って何万回も処理するときは、ちゃんと宣言して容量を節約いなければなりませんが、
たった2回ですから処理時間に与える影響は0.0001%以下でしょう。
宣言しなかったのは、時刻を入れる変数が何型であったか思い出せなかったからです。
ですから、VBAでは宣言しないで使うという手もあるわけです。
C言語やVC++では宣言しないと、必ずエラーするのとは対照的です。
もう20年以上、触れていないので記憶には自信がありませんが、
確かパスカル
(C言語同様構造化プログラミング思想の徹底した言語、現在はどうか知りませんが、
かつては日立製作所の社内の公式プログラム言語に指定されていました。)
はもエラーしたと思います。
VBA以外では、宣言しないとエラーすると考えて方がよいですね。
現在時刻を取得するときには、Timerを使うと覚えてください。
ow = Timer
も現在の時刻を取得しています。
hjは問題を解く前の時刻で、owは問題を解いた後の時刻ですから
Cells(3, 23) = ow - hj
によって問題を解くのに要した時間が測定できるわけです。
If m(x, y) > 0 Then
If g + 1 < 81 Then
f (g + 1)
Else
For j = 0 To 8
For k = 0 To 8
Cells(13 + j, 2 + k) = m(j, k)
Next
Next
cn = cn + 1
If cn = 1 Then Exit Sub
End If
Exit Sub
End If
時間測定を除けば、プログラムで加えた点はピンクのみに尽きます。
ピンクによって、数独自動生成プログラムが数独を解くソフトに変身するのです。
問題で与えてあるセルの数字は動かしてはいけません。
ですから、セルの数字が入っている場合スキップするようにしてあるわけです。
数字が入っている場合、無条件で次のセルに進むようにしてあるわけです。
忘れてはいけないのは、
Exit Sub
です。
これがないといけない理由は、
上のセルに進んでだめだった場合に、ここに戻ってきますが、
Exit Sub
がないと、
For i = 1 To 9
kkk = (kk + 4 * i) Mod 9 + 1
m(x, y) = kkk
For j = 0 To 8
If j <> x Then
If m(x, y) = m(j, y) Then GoTo tobi
End If
Next
For j = 0 To 8
If j <> y Then
If m(x, y) = m(x, j) Then GoTo tobi
End If
Next
For j = 0 To 2
For k = 0 To 2
If 3 * xs + j <> x And 3 * ys + k <> y Then
If m(x, y) = m(3 * xs + j, 3 * ys + k) Then GoTo tobi
End If
Next
Next
If g + 1 < 81 Then
f (g + 1)
Else
For j = 0 To 8
For k = 0 To 8
Cells(13 + j, 2 + k) = m(j, k)
Next
Next
cn = cn + 1
If cn = 1 Then Exit Sub
End If
If cn = 1 Then Exit Sub
tobi:
Next
によって、いじってはいけないセルをいじってしまいます。
問題時に入っていたセルの数字は動かしてはいけませんね。
要するに問題時に入っていたセルに来たら、
スキップして上のセルに進み、戻ってきたら何もせず前のセルに戻っているのです。
つまり、上方向の進むときも下方向に進むときもスキップしているのです。
下方向スキップの役割を持つコードが
Exit Sub
何もせず下の世界=セル番号が1つ若い世界に戻ることになりますね。
問題を解く過程で試行錯誤をするわけですから、
問題時に数字が入っていてセルを何回も通るわけです。
その時上方向の進んでいるときも、
下方向に進んでいるときもいずれもスキップしないと、
問題にはいっていたセルの数字を変えてしまうことになるわけです。
尚、最後から2行目の
m(x, y) = 0
も必須のものです。理由は、
If m(x, y) > 0 Then
にあります。0より大きければスキップしなさいとなっているわけですから、
試行錯誤でセルに仮に入れた数字は、
試行が失敗であったとわかった時点で0に戻しておかないと、
問題時に入っていた数字か、
問題を解く過程で仮に入れた数字かの区別が付かなくなってしまいます。
試行が失敗であったときは、仮に入れた数字をキャンセルして
元の世界=セル番号が1つ若い世界に戻らなければならないわけです。
以上でVer.0のコード解説は終わりにして、いよいよ改良してVer.1へと進化させる段取りとなります。
第2話へ 第4話へ
VBA講義第1部へ
vc++講義へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ
数学研究室に戻る