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

第12話 魔方陣作成マクロVer.4のコード解説
さて、第11話でお約束したとおり、Ver.4のコードを解説しましょう。
一番難しい点は、2つの一般種をいかに直交させるかです。
というより、直交条件を満たしながら2つめの一般種をいかに作るかが問題です。
もちろんVer.3のように、配列p(i)を利用するのが1つの手です。
Ver.3では、配列p(36)を用意して
Dim mah(6, 6) As Byte, x(36) As Byte, y(36) As Byte, p(36) As Byte
Dim n As Byte, cn As Integer
Private Sub CommandButton1_Click()
  Dim i, j As Integer
  n = Cells(3, 8)
  Rnd (-1)
  If n = 4 Then Randomize (425)
  If n = 5 Then Randomize (24)
  If n = 6 Then Randomize (4768)
  syokika
  zhy
  ms (0)
End Sub
Sub syokika()
  Dim i As Byte
  For i = 0 To n * n - 1
    p(i) = 0
  Next

  sk = 0
  cn = 0
End Sub
初期化においてすべて0に設定してから、
セルに数字を入れる度にp(mah(b, a) - 1) = 1としました。
5次魔方陣であればp(0) = 1、p(1) = 1、p(2) = 1、p(3) = 1、・・・、p(24) = 1、
となれば、25種類の数字1,2,3,・・・、25(この数字はp(i)のiに1つ加えたもの)が延べ25個あるので、
各数字は1つずつしかないとわかるわけです。
この配列p(i)を利用して、2番目の種mah2(b,a)が見つかる度に、
p(n*mah1(b, a) + mah2(b,a)) = 1として、25種類数字が延べ25個あるかを調べればよいのです。
ここで、mah1(b, a) とmah2(b,a)は、1つめの種と2つめの種に対応します。
この方法も立派なやり方です。
処理時間も、本方式とほぼ同じでしょう。
しかし、本方式では次のようなやり方をしてみました。
私が、20年ぐらい前に初めて作った方法で愛着があったからです。
それは、2次元配列
Dim th(12, 12) As Byteを用意し、それを初期化においてすべて0にセットしておきます。
そして、mah2(b,a)に数字を入れる度にth(mah1(b, a) ,mah2(b,a))=1とするのです。
5次魔方陣の場合th(i, j)は2種類存在します。
5×5=25であるからです。
つまり、

th(0, 0)、th(0, 1)、th(0, 2)、th(0, 3)、th(0, 4)
th(1, 0)、th(1, 1)、th(1, 2)、th(1, 3)、th(1, 4)
th(2, 0)、th(2, 1)、th(2, 2)、th(2, 3)、th(2, 4)
th(3, 0)、th(3, 1)、th(3, 2)、th(3, 3)、th(3, 4)
th(4, 0)、th(4, 1)、th(4, 2)、th(4, 3)、th(4, 4)
のすべてが1になれば、25種類
の数字が延べ25個存在することになります。
私が、20年ぐらい前にこれを作ったときのイメージは、格子点
(0,0)、(0,1)、(0,2)、(0,3)、(0,4)
(1,0)、(1,1)、(1,2)、(1,3)、(1,4)
(2,0)、(2,1)、(2,2)、(2,3)、(2,4)
(3,0)、(3,1)、(3,2)、(3,3)、(3,4)
(4,0)、(4,1)、(4,2)、(4,3)、(4,4)
のすべてに碁石が1つずつおければよいです。
25個の碁石がすべての格子点(25個)におければ、
各格子点には1つずつおかれていることになります。
つまり、(mah1(b,a),mah2(b,a))の2つの数字から構成されるn*mah1(b, a) + mah2(b,a) +1には重複がないことになります。
これで直交条件が満たされるのです。

一番重要な点は、解説が終わりました。
後は、細かい点の説明が残っているのみです。
msとms1やms2との違いは、3点です。
msにおいてはmah(b,a)に入れる数字が1から25であり、それぞれが1つずつであったのに対して、
mah1(b,a)とmah2(b,a)は、入れる数字が0から4で、それぞれが5つずつです。
5つというには、5次魔方陣の5です。
それで、ループ文がFor i = 0 To n - 1(msの場合For i = 0 To n * n - 1でした)や
If sa < 0 Or sa > (n - 1) Then Exit Sub(msの場合If sa < 1 Or sa > n * n Then Exit Subでした)になっています。
また、msの場合If p1(sa) = 1 Then Exit Subとなっていたのが、
If p1(sa) >= n Then Exit Subに変わっています。msでは1つずつであったのに対してms1とms2においてはn個ずつであるからです。
3点目の違いは、行・列・対角線合計の違いです。
msの場合は、行・列・対角線
合計はInt(n * n * (n + 1) / 2) でしたが、ms1(ms2)では Int(n * (n - 1) / 2) となっています。
一般種の合計は
0+1+2+3+・・・+nです。
この和は、n×(n−1)÷2です。

以上の3点、ms1とms2に分割されていることも違いと考えると4点がmsと異なる点で、
基本的な構造は全く同じです。

さて、次話ではシード値自動探索コードをお見せしましょう。




第11話へ 第13話へ

004
  

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

数学研究室に戻る