第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話へ

004
  

VBA講義第1部へ
vc++講義へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ

数学研究室に戻る