第18講 一般種法×末項確定法(魔方陣作成マクロVer.4)への挑戦

第15話 最適値自動探索プログラムの実験結果による最適値とそれによる改良
12次については、現在実験中です。
試行回数の設定が50000では結果が出なかったので、
100000に設定し直して実験しています。
実験結果が出るのに半日かかるので、それを待つしかありませんので、
12次については明日以降と言うことになります。
試行回数とは、プログラムコードの
If sk > 3000 Then Exit Sub
のskです。下の実験結果をご覧になればお分かりのように、6次までなら設定値は50で済むことになります。
この設定値を大きくすると時間がかかりますので、次数に応じて設定値を変更する必要があります。

11次までの実験結果をお見せしましょう。

次数 4 5 6 7 8 9 10 11 12
最小試行回数 0 5 48 178 373 1109 9802 11752
最適シード値 6763 6601 5084 9362 6337 5104 1763 436
最適素な数1 1 3 5 3 1 4 9 6
最適素な数2 3 1 1 3 3 1 9 6

尚、9〜11次においては実験を途中で打ち切ったので、for文は10000までは試していません。
For c = 1 To 10000
    h = 0
    kz = 0
    If n = 4 Then kk = 1
    If n = 5 Then kk = 3
    If n = 6 Then kk = 1
    If n = 7 Then kk = 5
    If n = 8 Then kk = 3
    If n = 9 Then kk = 5
    If n = 10 Then kk = 3
    If n = 11 Then kk = 10
    If n = 12 Then kk = 3
    For d = 0 To kk
      For e = 0 To kk
        syokika
        Randomize (c)
        ms1 (0)
        If cn = 1 Then h = 1
      Next
    Next
    If h = 1 Then
      Cells(6 + kz2, 1) = c
      kz2 = kz2 + 1
    End If
Next
10次からは、for文のどこまで進行したか表示するようにプログラムを変更しましたが、
その値を見るのを忘れて次の実験に入ってしまいましたので、
いくつまでの実験結果は報告できません。11次は9000ぐらいで打ち切りました。
11次はほぼ最後まで実験しましたから、最小試行回数は1万回程度でしょうが、
9次と10次に関しては、表の値より小さい可能性があります。
実験結果で驚きは、6次までの最小試行回数です。
4次に至っては、最適シード値と最適素な数による結果はなんと0です。
ということは、ストレートに4次魔方陣ができたということです。
試行回数0、適当に入れていったら行ったり来たりせず一発で答えが出てしまったわけです。
これはかなりの幸運といえます。

最適素な数1 と 最適素な数2 とは、下のコードの iii = (ii + ss * i) Mod n のssです。
何と素な関係にあるかと申しますと、nとです。
例えば、n=10のときは、nと素な数は1,3,7,9です。
10と素な数とは、10と共通の約数が1である数です。
ssが素な数である場合 iii = (ii + ss * i) Mod n はすべての場合を網羅します。
n=10,ss=3,ii=6でトレースしてみましょう。
iを0から9まで変えていくと、
iii=(6+3*0) Mod 10=6
iii=(6+3*1) Mod 10=9
iii=(6+3*2) Mod 10=2
iii=(6+3*3) Mod 10=5
iii=(6+3*4) Mod 10=8
iii=(6+3*5) Mod 10=1
iii=(6+3*6) Mod 10=4
iii=(6+3*7) Mod 10=7
iii=(6+3*8) Mod 10=0
iii=(6+3*9) Mod 10=3
6,9,2,5,8,1,4,7,0,3と0から9まで確かにすべて重複なしに網羅しています。
しかも、現れ方はかなりランダムのように見えます(実際は3飛びに規則的)。
皆さん、10と素な関係にない4などでトレースしてみてください。
すると、重複なしに網羅していないことがわかります。
10と素な数でない2,4,5,6,8はすべて重複なしに網羅するの条件を満たしません。

実験結果によるVer.4の改良コードをお見せします。
改良部分をで示しておきます。
Dim mah1(12, 12) As Byte, mah2(12, 12) As Byte, x(144) As Byte, y(144) As Byte, p1(144) As Byte, p2(144) As Byte, a(12, 12) As Integer
Dim th(12, 12) As Byte
Dim n As Byte, cn As Integer, gk As Integer, tm As Byte
Private Sub CommandButton1_Click()
  Dim i, j As Integer
  n = Cells(3, 8)
  syokika
  zhy
  zhy1 (0)
  'zhykakunin
  syokika1
  ms1 (0)
End Sub
Sub syokika()
  Dim i As Byte
  Dim j As Byte
  For i = 0 To n - 1
    p1(i) = 0
    p2(i) = 0
    For j = 0 To n - 1
      th(i, j) = 0
    Next
  Next
  tm = 0
  sk = 0
  cn = 0
  gk = n
  For i = 0 To n - 1
    For j = 0 To n - 1
      a(i, j) = -1
    Next
  Next
End Sub
Sub syokika1()
  Dim i As Byte
  Dim j As Byte
  For i = 0 To n - 1
    For j = 0 To n - 1
      mah1(i, j) = 0
      mah2(i, j) = 0
    Next
  Next
  Rnd (-1)
  If n = 4 Then Randomize (6763)
  If n = 5 Then Randomize (6601)
  If n = 6 Then Randomize (5084)
  If n = 7 Then Randomize (9362)
  If n = 8 Then Randomize (6337)
  If n = 9 Then Randomize (5104)
  If n = 10 Then Randomize (1763)
  If n = 11 Then Randomize (436)
End Sub


Sub zhy()

  Dim i As Byte
  
  For i = 0 To n - 1
    a(i, i) = i
  Next
  For i = 0 To n - 1
    If a(i, n - 1 - i) = -1 Then
      a(i, n - 1 - i) = gk
      gk = gk + 1
    End If
  Next
  
End Sub
Sub zhy1(g As Byte)
  Dim i As Byte
  For i = g + 1 To n - 1
    If a(g, i) = -1 Then
      a(g, i) = gk
      gk = gk + 1
    End If
  Next
  zhy2 (g)
  If tm = 1 Then Exit Sub
End Sub
Sub zhy2(g As Byte)
  Dim i As Byte
  Dim j As Byte
  For i = g + 1 To n - 1
    If a(i, g) = -1 Then
      a(i, g) = gk
      gk = gk + 1
    End If
  Next
  If g + 1 < n Then
    zhy1 (g + 1)
    If tm = 1 Then Exit Sub
  Else
    For i = 0 To n - 1
      For j = 0 To n - 1
        y(a(i, j)) = i
        x(a(i, j)) = j
      Next
    Next
    tm = 1
    Exit Sub
  End If
    
End Sub

Sub zhykakunin()
  Dim i As Byte
  
  For i = 0 To n * n - 1
    Cells(5 + y(i), 1 + x(i)) = i
  Next
  
End Sub

Sub ms1(g As Byte)

  Dim i As Integer, j As Byte, k As Byte, a As Byte, b As Byte, w As Integer, sa As Integer, h As Byte
  Dim ii As Integer, iii As Integer, kk As Byte

  a = x(g)
  b = y(g)
  If a = n - 1 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah1(i, i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    ms1 (g + 1)
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  If a = 0 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah1(i, n - 1 - i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    ms1 (g + 1)
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  If a = n - 2 And b = 0 Then
    w = 0
    For i = 0 To n - 3
      w = w + mah1(b, i)
    Next
    w = w + mah1(b, n - 1)
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    ms1 (g + 1)
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  If a = 0 And b = n - 2 Then
    w = 0
    For i = 0 To n - 3
      w = w + mah1(i, a)
    Next
    w = w + mah1(n - 1, a)
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    ms1 (g + 1)
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  If a = n - 1 And b > 0 And b < n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah1(b, i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    ms1 (g + 1)
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  If a > 0 And a < n - 1 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah1(i, a)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If p1(sa) >= n Then Exit Sub
    mah1(b, a) = sa
    p1(sa) = p1(sa) + 1
    If g + 1 < n * n Then
      ms1 (g + 1)
    Else
      ms2 (0)
    End If
    p1(sa) = p1(sa) - 1
    Exit Sub
  End If
  
  ii = n * Rnd
  Dim ss As Byte
  If n = 4 Then ss = 1
  If n = 5 Then ss = 3
  If n = 6 Then ss = 5
  If n = 7 Then ss = 3
  If n = 8 Then ss = 1
  If n = 9 Then ss = 4
  If n = 10 Then ss = 9
  If n = 11 Then ss = 6

  For i = 0 To n - 1
    iii = (ii + ss * i) Mod n
    mah1(b, a) = iii
    h = 0
    If p1(mah1(b, a)) >= n Then GoTo tobi
    p1(mah1(b, a)) = p1(mah1(b, a)) + 1
    h = 1
    ms1 (g + 1)
tobi:
    If h = 1 Then p1(mah1(b, a)) = p1(mah1(b, a)) - 1
  Next
End Sub
Sub ms2(g As Byte)
  Dim i As Integer, j As Byte, k As Byte, a As Byte, b As Byte, w As Integer, sa As Integer, h As Byte
  Dim ii As Integer, iii As Integer, kk As Byte, hs As Integer
  a = x(g)
  b = y(g)
  hs = mah1(b, a)
  If a = n - 1 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah2(i, i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    ms2 (g + 1)
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  If a = 0 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah2(i, n - 1 - i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    ms2 (g + 1)
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  If a = n - 2 And b = 0 Then
    w = 0
    For i = 0 To n - 3
      w = w + mah2(b, i)
    Next
    w = w + mah2(b, n - 1)
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    ms2 (g + 1)
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  If a = 0 And b = n - 2 Then
    w = 0
    For i = 0 To n - 3
      w = w + mah2(i, a)
    Next
    w = w + mah2(n - 1, a)
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    ms2 (g + 1)
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  If a = n - 1 And b > 0 And b < n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah2(b, i)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    ms2 (g + 1)
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  If a > 0 And a < n - 1 And b = n - 1 Then
    w = 0
    For i = 0 To n - 2
      w = w + mah2(i, a)
    Next
    sa = Int(n * (n - 1) / 2) - w
    If sa < 0 Or sa > (n - 1) Then Exit Sub
    If th(mah1(b, a), sa) = 1 Then Exit Sub
    If p2(sa) >= n Then Exit Sub
    mah2(b, a) = sa
    p2(sa) = p2(sa) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    If g + 1 < n * n Then
      ms2 (g + 1)
    Else
      For j = 0 To n - 1
        For k = 0 To n - 1
          Cells(6 + k + Int(cn / 10) * (n + 1), 1 + j + (cn Mod 10) * (n + 1)) = n * mah1(j, k) + mah2(j, k) + 1
        Next
      Next
      cn = cn + 1
      Cells(4, 12) = cn
    End If
    p2(sa) = p2(sa) - 1
    th(mah1(b, a), mah2(b, a)) = 0
    Exit Sub
  End If
  ii = n * Rnd
  Dim ss As Byte
  If n = 4 Then ss = 3
  If n = 5 Then ss = 1
  If n = 6 Then ss = 1
  If n = 7 Then ss = 3
  If n = 8 Then ss = 3
  If n = 9 Then ss = 1
  If n = 10 Then ss = 9
  If n = 11 Then ss = 6

  For i = 0 To n - 1
    iii = (ii + ss * i) Mod n
    mah2(b, a) = iii
    h = 0
    If p2(mah2(b, a)) >= n Then GoTo tobi
    If th(mah1(b, a), mah2(b, a)) = 1 Then GoTo tobi
    p2(mah2(b, a)) = p2(mah2(b, a)) + 1
    th(mah1(b, a), mah2(b, a)) = 1
    h = 1
    ms2 (g + 1)
tobi:
    If h = 1 Then
      p2(mah2(b, a)) = p2(mah2(b, a)) - 1
      th(mah1(b, a), mah2(b, a)) = 0
    End If
  Next
End Sub

Private Sub CommandButton2_Click()
  Rows("5:2000").Select
  Selection.ClearContents
  Cells(4, 12).Select
  Selection.ClearContents
  Cells(1, 1).Select
End Sub

ダウンロード用参考ファイル

ダウンロード用ファイルをクリックして、改良によってとても速くなったことを体感して下さい。
さて、皆さん時間を計測できるようにさらに改善しましょう。

第14話へ 第16話へ

004
  

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

数学研究室に戻る