第16講 魔方陣作成の高速化(数万倍へ)
第5話 魔方陣作成マクロVer.2の完成二歩前とコード解説
コード例
Sub ms(g As Byte)
Dim i As Byte, j As Byte, k As Byte, a As Byte, b As Byte, w As Byte, ii As Byte, iii As Byte
a = x(g)
b = y(g)
ii = Int((n * n) * Rnd)
For i = 0 To n * n - 1
iii = (ii + i) Mod n * n
mah(b, a) = iii + 1
・
・
・
と改善すればよいのです。
『でも、実際には6次で実験すると、1時間待っても出てこないではないですか。』
ですって。
その通りです。
ですが、
Private Sub CommandButton1_Click()
Randomize (・)
・
・
・
Randomize (・)を入れて・のところに正の整数を入れ、乱数系列を変えていけば、1秒もかからず1個作るのは可能になります。
しかし、実験は6次になると数回の実験では済みません。数千回の実験が必要です。
そんなことを手でやるのはもちろん無駄です。
第8話で自動実験に挑戦します。
実験結果を踏まえて、今回のコードを改善してVer.2の完成となります。
ですが、完成しても1秒で数百個作るのは、Ver.2では不可能です。
それで、末項確定法、一般種法とVerをあげていきます。
私の記憶では、末項確定法で確か7次が可能になり、一般種法で12次まで可能になります。
26次あたりを1秒で数百の単位で作らせるには、まだまだ改善が必要です。
細胞構成法までバージョンが上がると、この夢が実現します。
さて、ここでは第3話のコード
Sub zhy()
Dim i, k As Byte
Dim a(6, 6) As Integer
For i
= 0 To n - 1
For j = 0 To n - 1
a(i, j) =
-1
Next
Next
For i = 0 To n - 1
a(i, i) =
i
Next
k = n
For i = 0 To n - 1
If a(i, n - 1 - i) = -1 Then
a(i, n - 1 - i) = k
k = k + 1
End
If
Next
For i = 0 To n - 1
For j = 0 To n - 1
If a(i,
j) = -1 Then
a(i, j) = k
k = k + 1
End
If
Next
Next
For i = 0 To n - 1
For j = 0 To n -
1
x(a(i, j)) = j
y(a(i, j)) =
i
Next
Next
End Sub
の解説を行います。
第4話のコードの解説は、第6話以降で数話かけて行う予定です。
このコードを見る度に、仮屋崎さんは天才だと思います。
本当にすばらしいコードです。
後で、私が考えたコードもお見せしますが、
仮屋崎さんのコードの方がずっとすっきりしています。
さらに、仮屋崎さんのコードは一般法の複雑な番号付け
0 | 1 | 2 | 3 | |
0 | 0 | 8 | 9 | 4 |
1 | 10 | 1 | 5 | 12 |
2 | 11 | 6 | 2 | 14 |
3 | 7 | 13 | 15 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | 9 | 10 | 11 | 5 |
1 | 12 | 1 | 15 | 6 | 16 |
2 | 13 | 17 | 2 | 19 | 20 |
3 | 14 | 7 | 21 | 3 | 23 |
4 | 8 | 18 | 22 | 24 | 4 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
0 | 0 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 10 |
1 | 28 | 1 | 36 | 37 | 38 | 39 | 40 | 41 | 11 | 42 |
2 | 29 | 43 | 2 | 50 | 51 | 52 | 53 | 12 | 54 | 55 |
3 | 30 | 44 | 56 | 3 | 62 | 63 | 13 | 64 | 65 | 66 |
4 | 31 | 45 | 57 | 67 | 4 | 14 | 72 | 73 | 74 | 75 |
5 | 32 | 46 | 58 | 68 | 15 | 5 | 80 | 81 | 82 | 83 |
6 | 33 | 47 | 59 | 16 | 76 | 84 | 6 | 88 | 89 | 90 |
7 | 34 | 48 | 17 | 69 | 77 | 85 | 91 | 7 | 94 | 95 |
8 | 35 | 18 | 60 | 70 | 78 | 86 | 92 | 96 | 8 | 98 |
9 | 19 | 49 | 61 | 71 | 79 | 87 | 93 | 97 | 99 | 9 |
のときにまた威力を発揮します。この番号付け見たら、とても一般化など無理なように見えますが、
仮屋崎さんの方法を使うと割と簡単にできます。
さて、話を戻しましょう。入力順
0 | 1 | 2 | 3 | |
0 | 0 | 8 | 9 | 4 |
1 | 10 | 1 | 5 | 11 |
2 | 12 | 6 | 2 | 13 |
3 | 7 | 14 | 15 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | 9 | 10 | 11 | 5 |
1 | 12 | 1 | 13 | 6 | 14 |
2 | 15 | 16 | 2 | 17 | 18 |
3 | 19 | 7 | 20 | 3 | 21 |
4 | 8 | 22 | 23 | 24 | 4 |
を構築するにはどうしたらよいでしょうか。
まず、
For i
= 0 To n - 1
For j = 0 To n - 1
a(i, j) = -1
Next
Next
は
@ 各座標から番号への準備です。
つまり、a(i, j)に番号を割り振るための準備です。とりあえず、
0 | 1 | 2 | 3 | |
0 | -1 | -1 | -1 | -1 |
1 | -1 | -1 | -1 | -1 |
2 | -1 | -1 | -1 | -1 |
3 | -1 | -1 | -1 | -1 |
0 | 1 | 2 | 3 | 4 | |
0 | -1 | -1 | -1 | -1 | -1 |
1 | -1 | -1 | -1 | -1 | -1 |
2 | -1 | -1 | -1 | -1 | -1 |
3 | -1 | -1 | -1 | -1 | -1 |
4 | -1 | -1 | -1 | -1 | -1 |
としています。これは、数字の重複を防ぐための処理です。
つまり、まだ番号付けがなされていない印として−1を割り振ったのです。
ただの印ですから、絶対に入らない数字なら何でもよかったのです。
例えば、5次魔方陣なら最後の番号は24ですから、25でもよいのです。
−1の印があるセルは、まだ番号付けがされていないので番号付けをすべき対象だということです。
例えば
0 | 1 | 2 | 3 | |
0 | 0 | -1 | -1 | -1 |
1 | -1 | 1 | -1 | -1 |
2 | -1 | -1 | -1 | -1 |
3 | -1 | -1 | -1 | -1 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | -1 |
1 | -1 | 1 | -1 | -1 | -1 |
2 | -1 | -1 | -1 | -1 | -1 |
3 | -1 | -1 | -1 | -1 | -1 |
4 | -1 | -1 | -1 | -1 | -1 |
となれば、座標(0,0)と(1,1)にはすでに番号が割り振られているので、番号を割り振ってはいけないということになります。
If a(i, n - 1 - i) = -1 Then は印が−1なら番号を割り振りなさいという意味なのです。
これで1つのセルに重複して番号を割り振ってしまう心配がないです。
最初の番号割り振り
For i = 0 To n - 1
a(i, i) =
i
Next
のときに、If a(i, n - 1 - i) = -1 Then がない理由は明らかに対角線上には数字が入っていないので重複検査をする必要がありません。
しかし、次の逆対角線入力において
k = n
For i = 0 To n - 1
If a(i, n - 1 - i) = -1 Then
a(i, n - 1 - i) = k
k = k + 1
End
If
Next
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | 5 |
1 | -1 | 1 | -1 | 6 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | -1 | -1 | 3 | -1 |
4 | -1 | -1 | -1 | -1 | 4 |
座標(2,2)に進む際に
If a(i, n - 1 - i) = -1 Then がないと
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | 5 |
1 | -1 | 1 | -1 | 6 | -1 |
2 | -1 | -1 | 7 | -1 | -1 |
3 | -1 | -1 | -1 | 3 | -1 |
4 | -1 | -1 | -1 | -1 | 4 |
と重複して番号を割り振ってしまいます。
すでに2が入っていて−1ではないので
If a(i, n - 1 - i) = -1 Then
a(i, n - 1 - i) = k
k = k + 1
End
If
その心配はなくて
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | 5 |
1 | -1 | 1 | -1 | 6 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | 7 | -1 | 3 | -1 |
4 | -1 | -1 | -1 | -1 | 4 |
等うまく割り振れます。
k = n
For i = 0 To n - 1
If a(i, n - 1 - i) = -1 Then
a(i, n - 1 - i) = k
k = k + 1
End
If
Next
のkの意味お分かりですね。最初の対角線割り振り
For i = 0 To n - 1
For j = 0 To n - 1
a(i, j) = -1
Next
Next
によって、
0 | 1 | 2 | 3 | |
0 | 0 | -1 | -1 | -1 |
1 | -1 | 1 | -1 | -1 |
2 | -1 | -1 | 2 | -1 |
3 | -1 | -1 | -1 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | -1 |
1 | -1 | 1 | -1 | -1 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | -1 | -1 | 3 | -1 |
4 | -1 | -1 | -1 | -1 | 4 |
番号は、n−1まで割り振られています。なので、k = nです。
そして、番号を割り振った後
0 | 1 | 2 | 3 | |
0 | 0 | -1 | -1 | 4 |
1 | -1 | 1 | -1 | -1 |
2 | -1 | -1 | 2 | -1 |
3 | -1 | -1 | -1 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | 5 |
1 | -1 | 1 | -1 | -1 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | -1 | -1 | 3 | -1 |
4 | -1 | -1 | -1 | -1 | 4 |
k = k + 1によって、kはn+1となります。もし、
k = k + 1
a(i, n - 1 - i) = k
と逆順にするなら、k = n - 1としなければなりませんね。
次の
For i = 0 To n - 1
For j = 0 To n - 1
If a(i,
j) = -1 Then
a(i, j) = k
k = k + 1
End
If
Next
Next
は、
0 | 1 | 2 | 3 | |
0 | 0 | -1 | -1 | 4 |
1 | -1 | 1 | 5 | -1 |
2 | -1 | 6 | 2 | -1 |
3 | 7 | -1 | -1 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | -1 | -1 | -1 | 5 |
1 | -1 | 1 | -1 | 6 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | 7 | -1 | 3 | -1 |
4 | 8 | -1 | -1 | -1 | 4 |
以降の割り振りです。ここでもIf a(i, n - 1 - i) = -1 Then が威力を発揮し座標(0,0)などへの重複割り振りを防いでいます。
0 | 1 | 2 | 3 | |
0 | 0 | 8 | 9 | 4 |
1 | 10 | 1 | 5 | -1 |
2 | -1 | 6 | 2 | -1 |
3 | 7 | -1 | -1 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | 9 | 10 | 11 | 5 |
1 | 12 | 1 | -1 | 6 | -1 |
2 | -1 | -1 | 2 | -1 | -1 |
3 | -1 | 7 | -1 | 3 | -1 |
4 | 8 | -1 | -1 | -1 | 4 |
0 | 1 | 2 | 3 | |
0 | 0 | 8 | 9 | 4 |
1 | 10 | 1 | 5 | 11 |
2 | 12 | 6 | 2 | 13 |
3 | 7 | 14 | 15 | 3 |
0 | 1 | 2 | 3 | 4 | |
0 | 0 | 9 | 10 | 11 | 5 |
1 | 12 | 1 | 13 | 6 | 14 |
2 | 15 | 16 | 2 | 17 | 18 |
3 | 19 | 7 | 20 | 3 | 21 |
4 | 8 | 22 | 23 | 24 | 4 |
(赤の番号はx、濃紺の番号はy)
さて、最後の
For i = 0 To n - 1
For j = 0 To n -
1
x() = j
y(a(i, j)) = i
Next
Next
は、A 番号から各座標へを行っています。
ここがこのコードの眼目です。
x(a(i, j)) = j おいては番号をx座標に対応させています。
例えば、5次魔方陣の19という番号は、
a(3, 0)=19
x(a(3, 0)) = 3
ですから、確かに3に対応させられまています。y座標についても同じですね。
a(3, 0)=19
y(a(3, 0)) = 0
19が0に対応させられます。
どうです。実に見事でしょう。
第4話へ 第6話へ
VBA講義第1部へ
vc++講義へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ
数学研究室に戻る