第16講 魔方陣作成の高速化(数万倍へ)

第8話 自動実験コード解説
コード再掲
Dim mah(6, 6) As Byte, x(36) As Byte, y(36), sh(20) As Byte, d As Byte
Dim n As Byte, cn As Integer
Dim sk As Long, c As Long    ’試行回数を数える変数sk、Randomizeの番号(シード値)を指定するグローバル変数c
Dim kz As Byte, kz2 As Integer   '列位置を整える変数kz、行位置を揃える変数kz2
Private Sub CommandButton1_Click()
  Dim i, j As Integer
  n = Cells(3, 8)
  If n = 4 Then         'この行から
    sh(0) = 1
    sh(1) = 3
    sh(2) = 5
    sh(3) = 7
    sh(4) = 9
    sh(5) = 11
    sh(6) = 13
    sh(7) = 15
  End If
  If n = 5 Then
    sh(0) = 1
    sh(1) = 2
    sh(2) = 3
    sh(3) = 4
    sh(4) = 6
    sh(5) = 7
    sh(6) = 8
    sh(7) = 9
    sh(8) = 11
    sh(9) = 12
    sh(10) = 13
    sh(11) = 14
    sh(12) = 16
    sh(13) = 17
    sh(14) = 18
    sh(15) = 19
    sh(16) = 21
    sh(17) = 22
    sh(18) = 23
    sh(19) = 24
  End If
  If n = 6 Then
   sh(0) = 1
   sh(1) = 5
   sh(2) = 7
   sh(3) = 11
   sh(4) = 13
   sh(5) = 17
   sh(6) = 19
   sh(7) = 23
   sh(8) = 25
   sh(9) = 29
   sh(10) = 31
   sh(11) = 35
  End If              'この行までは、n*nと互いな素な(つまり、n*nと1以外の公約数をもたない)整数を指定している。


  zhy

  kz2 = 0
  For c = 1 To 10000
    h = 0
    If n = 4 Then kk = 7
    If n = 5 Then kk = 19
    If n = 6 Then kk = 11
    kz = 0
    For d = 0 To kk
      sk = 0
      cn = 0
      Rnd (-1)          '乱数系列の初期化、これがないとRandomizeで番号を指定しても反映されない。
      Randomize (c)       '乱数系列の指定。Rnd(-1)とセットで使う。
      ms (0)
      If cn = 1 Then h = 1
    Next
    If h = 1 Then
      Cells(5 + kz2, 1) = c
      kz2 = kz2 + 1  '行位置調整
    End If
  Next

End Sub
        ・
        ・
        ・
Sub ms(g As Byte)
  Dim i As Integer, j As Byte, k As Byte, a As Byte, b As Byte, w As Byte
  Dim ii As Integer, iii As Integer
  
If sk > 10000 Then Exit Sub    '試行回数が上限を超えたら、思考を止めFor文の次に進めるようにしてあります。
  If cn = 1 Then Exit Sub
       '魔方陣が見つかったら止めるようにしてあります。
  a = x(g)
  b = y(g)
  ii = n * n * Rnd
  For i = 0 To n * n - 1
    
iii = (ii + sh(d) * i) Mod n * n
    mah(b, a) = iii + 1
        ・
        ・
        ・
    If g + 1 < n * n Then
      sk = sk + 1
      ms (g + 1)
    Else
     
 'For j = 0 To n - 1     '以降の6行は、魔方陣を表示させないように'を入れ注釈文に変えられています。
        'For k = 0 To n - 1   'もちろん削除してもいいわけですが、復元を考えて注釈文にしました。
          'Cells(6 + k + Int(cn / 10) * (n + 1), 1 + j + (cn Mod 10) * (n + 1)) = mah(j, k)
        'Next
      'Next

      cn = cn + 1
      
Cells(5 + kz2, 3 + 2 * kz) = d
      Cells(5 + kz2, 4 + 2 * kz) = sk
      kz = kz + 1  '列位置調整
      If cn = 1 Then Exit Sub
  '魔方陣が見つかったら止めるようにしてあります。
    End If
tobi:
  Next

End Sub

解説
  If n = 4 Then         
    sh(0) = 1
    sh(1) = 3
    sh(2) = 5
    sh(3) = 7
    sh(4) = 9
    sh(5) = 11
    sh(6) = 13
    sh(7) = 15
  End If

は16と
  If n = 5 Then
    sh(0) = 1
    sh(1) = 2
    sh(2) = 3
    sh(3) = 4
    sh(4) = 6
    sh(5) = 7
    sh(6) = 8
    sh(7) = 9
    sh(8) = 11
    sh(9) = 12
    sh(10) = 13
    sh(11) = 14
    sh(12) = 16
    sh(13) = 17
    sh(14) = 18
    sh(15) = 19
    sh(16) = 21
    sh(17) = 22
    sh(18) = 23
    sh(19) = 24
  End If

は25と
  If n = 6 Then
   sh(0) = 1
   sh(1) = 5
   sh(2) = 7
   sh(3) = 11
   sh(4) = 13
   sh(5) = 17
   sh(6) = 19
   sh(7) = 23
   sh(8) = 25
   sh(9) = 29
   sh(10) = 31
   sh(11) = 35
  End If           

は36と1以外の公約数をもたない整数を列挙しています。
今は、n=4,5,6の場合の3つなので具体的に数えていますが、
一般化する場合は、互いに素な整数を見つけるSubプロシージャを作ってコンピュータに探させるとよいでしょう。

  kz2 = 0
  For c = 1 To 10000
    h = 0
    If n = 4 Then kk = 7
    If n = 5 Then kk = 19
    If n = 6 Then kk = 11
    kz = 0
    For d = 0 To kk
      sk = 0
      cn = 0
      Rnd (-1)          '乱数系列の初期化、これがないとRandomizeで番号を指定しても反映されない。
      Randomize (c)       '乱数系列の指定。Rnd(-1)とセットで使う。
      ms (0)
      If cn = 1 Then h = 1
    Next
    If h = 1 Then
      Cells(5 + kz2, 1) = c
      kz2 = kz2 + 1  '行位置調整
    End If
  Next
ここからは、Randomize ()の
と iii = (ii + sh() * i) Mod n * nのによる2次元ループに入っています。
を担当しているのが
  For c = 1 To 10000
       ・
  Next

であり、
を担当しているのが
    For d = 0 To kk
       ・
    Next

です。
cもdもSubプロシージャmsにおいても使うので、グローバル変数にしてあります。
わかりにくいのが、kzとkz2の役割です。
kzは列位置、kz2は行位置を調整しています。
初心者
つまり、上限試行回数(上図の例では10000)以内に魔方陣が見つかったときだけ、kzはカウントしています。
kz2に関しては、sh(d)においてどのdでもひとつでも魔方陣が見つかったときだけカウントしています。
こうすることによって無駄なスペースなく、結果を上図のように表示できているわけです。





第7話へ 第9話へ

004
  

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

数学研究室に戻る