魔法陣再び
ここ二三日,頭の中が魔法陣に浸食されています.プログラミング学習の手始めに魔法陣を作りましたが,アルゴリズムの部分が作り方の手順を示しただけであり,なぜ魔法陣になるか? を示していません.一度,総当たりで魔法陣を作ってみようか? それであれば,奇数・偶数に関係なく作ることができます・・・などなど.
偶数次の魔法陣にも作り方があって,例えば \(4 \times 4\) の魔法陣は下のようになります.
\(4 \times 4\) のマス目に \(1\) から \(16\) の数字を順に書いて,対角に位置する \(1\) と \(16\),\(4\) と \(13\),\(6\) と \(11\),\(7\) と \(10\) を入れ替えると出来上がります.Excel を用いて,縦・横・対角 の合計を計算してあります.枠外の数字 \(34\) がそうです.確かに,魔法陣になっていますでしょ?
ところが,私の頭の中は「自分で魔法陣を作りたい」なのです.あまり考えたくないので(笑),方法は問わず兎に角自力で作りたい,と我儘を言っております.
Excel の画面を見ながら,ふとやってみたいことが浮かびました.\(1\) ~ \(9\) の数字から3つを選んで,その和が \(15\) になる組は何通りあるのか? これを調べてみたくなりました.
そもそも,その取り出し方が\[_{9}\!\mbox{C}_{3} = \frac{9 \cdot 8 \cdot 7}{3 \cdot 2 \cdot 1} = 84\]これだけの通りしかないということを考えると,魔法陣ができること自体不思議な気がします.そこで,次のマクロを作りました.
Sub ms3()
Dim i As Integer, j As Integer, k As Integer, raw As Integer
raw = 1
For i = 1 To 7
For j = i + 1 To 8
For k = j + 1 To 9
If i + j + k = 15 Then
Cells(raw, 7) = i
Cells(raw, 8) = j
Cells(raw, 9) = k
raw = raw + 1
End If
Next k
Next j
Next i
raw = raw - 1
For i = 1 To 9
Cells(i, 12) = i
Cells(i, 13) = WorksheetFunction.CountIf(Range(Cells(1, 7), Cells(raw, 9)), i)
Next i
End Sub
実行すると,このようになります.
G列~I列に書かれているのが,和が \(15\) になる組み合わせです.たった8組しかありません.横が3行,縦が3列,対角が2組,合計して8です.たった8組しかない組み合わせを,上手く並べると8つの「和が \(15\)である」数の並びになる,これって奇跡? と思ってしまいます.
L列とM列は,\(1\) ~ \(9\) の各数字が何回登場しているか?を示しています.4回登場する \(5\) の位置は,\(3 \times 3\) のど真ん中であると決定します.縦・横・2つの対角,それぞれに関係しますからね.
\(\begin{array}{|c|c|c|} \hline \quad & \quad & \quad \\ \hline \quad & \textcolor{red}{5} & \quad \\ \hline \quad & \quad & \quad \\ \hline \end{array}\)
次に左上のマスに入る数を決定しましょう.四隅には,登場回数3の \(2\),\(4\),\(6\),\(8\) のいずれかが入りますから,\(2\) ということにしましょう.\(2\) をどこに入れても,回転すれば結局は同じです.すると右下は自動的に \(8\) と決まります.
\(\begin{array}{|c|c|c|} \hline \textcolor{red}{2} & \quad & \quad \\ \hline \quad & 5 & \quad \\ \hline \quad & \quad & \textcolor{red}{8} \\ \hline \end{array}\)
残り2つの隅には,登場回数3の \(4\) と \(6\) が入ります.どちらに入れても,対角で折り返せば同じになってしまいます.
\(\begin{array}{|c|c|c|} \hline 2 & \quad & \textcolor{red}{4} \\ \hline \quad & 5 & \quad \\ \hline \textcolor{red}{6} & \quad & 8 \\ \hline \end{array}\)
残りの4マスは,行と列の合計が \(15\) になるよう自動的に決まります.
\(\begin{array}{|c|c|c|} \hline \ 2\ & \ \textcolor{red}{9}\ & \ 4\ \\ \hline \textcolor{red}{7} & 5 & \textcolor{red}{3} \\ \hline 6 & \textcolor{red}{1} & 8 \\ \hline \end{array}\)
回転と鏡像変換で一致するものを一つと考えるなら,\(3 \times 3\) の魔法陣は1種類しかない,と言えそうです.
え~! \(4 \times 4\) の場合も計算してみました.そうしましたら,和が \(34\) になる数の組み合わせは 86 通りあり,その中には \(1\) ~ \(16\) の数が \(19\) 回 ~ \(23\) 回も登場します.諦めました
30年近く前,教育センターに勤務していたとき,学校に Windows の PC が入っても授業で使えるソフトがないと言われ,「表計算ソフトは使えますよ.多少の工夫をすれば,関数のグラフを描いたり,描いたグラフを動かしたりもできますし.」と答えていました.
順列・組合の基本は数え上げ,と言います.こんな使い方もできるのですねぇ.しかも,お気楽に使えます.
使ったコマンドの確認
繰り返し: For 文
For var = num1 To num2 Step num3
繰り返し処理
Next var
var は変数であり,予約語でなければ何を使っても構いません.変数 var を num1 から num2 まで num3 置きに変化しながら,繰り返しの処理をします.Step num3 は省略でき,その際は Step 1 と見なされます.
コマンドの大文字,小文字は気にする必要がありません.エディタが自動的に修正しますので,私はすべて小文字で打ち込みます.
条件分岐: If 文
If 条件式 Then
条件式が true の処理
End If
If 条件式 Then
条件式が true の処理
Else
条件式が false の処理
End If
If 条件式1 Then
条件式1が true の処理
ElseIf 条件式2 then
条件式1が false で,条件式2が true の処理
End If
※なお,ElseIf は End If の前に複数使うことができます.
WorksheetFunction.CountIf(範囲,条件)
WorksheetFunction. に続けて,ワークシート内で用いる関数を書くことができます.Excel の豊富な関数を用いることができ,重宝します.
CountIf は指定された範囲内で,条件にあったセルが幾つあるかを返します.上のマクロでは,条件に i とだけ書かれています.これは,セルの内容が i に一致するものの個数を返します.条件は "文字列" や ">=0" なども使え,後者はセル内容が \(0\) 以上であるものの個数を返します.また,前者の文字列にはワイルドカードを用いることもできます.つまり部分一致の検索ができるということです.
Cells(行番号,列番号)
Excelは,セル番号に通常 A1 や C5 などを使いますが,VBA では Cells(行番号,列番号) を使うこともできます.繰り返し処理などでこちらの記述が使いやすいので,私は専らこちらを使います.配列やリストのように使えます.
Range(セル番号,セル番号)
選択したセルの範囲を表し,WorksheetFunction を使う際によくお世話になります.上述したように,私は Cells をつかうので,ワークシート内で
Basic は勿論のこと,Visual Basic も二十数年使わなかったら,Basic をすっかり忘れていました.記述が楽だと改めて思い至りました.
この記事を書いたところ,総当たりで魔法陣を作る,ということを益々やってみたくなりました.そのためには,\(1\) ~ \(n^2\) の数を並べたリストを,辞書式にすべての並びで書き出す必要があります.リストからある数を削除して別の場所に挿入するという作業を,再帰的に行えばできるだろうと考えています.リスト処理が得意であることからすると Python でしょうかねぇ.気になるのは,数が膨大になるので,処理の時間が大丈夫だろうか? です.素人の私には,最近の PC にどの程度の処理能力があるのか分かりません.処理速度をとるならC言語です.ん~!何かワクワクしている私は変態?