偶数の魔方陣を作成する

魔方陣とは1~Nまでの数字を「縦、横の合計が等しくなる」ように並べた行列です。
さらに縦、横だけでなく「斜めの合計も等しくなる」ような魔方陣もあります。
また上下左右に4等分すると、それぞれの合計も等しくなる(16+2+5+11, 3+13+10+8, 9+7+4+14, 6+12+15+1) ものや、さらに対角線からはずれた斜め方向(2+5+12+15, 3+8+9+14)でも、四つの数字の和が等しくなる 完全魔方陣もあります。
4,8,12,16, ...などの偶数の魔方陣を作成します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

魔方陣のアルゴリズム

4*4の魔方陣は以下の方法で作成することができます。
  1. 4*4の魔方陣を上下に分けて、上段には1~16の数を下段には16~1の数を格納します。
  2. 魔方陣を順にサーチして「対角要素には下の段を、それ以外は上の段の数字を選択します。
  3. 対角要素とは、対角線上の[0,0][1,1][2,2][3,3][0,3][1,2][2,1][3,0]のことで下図で○印が付いている箇所です。

  4. 8*8や16*16のときの対角要素の判定は4*4を組み合わせて下さい。
    例えば[0,3][1,2] だけで無く [0,4][1,5][3,7][4,0]なども対角要素に加えます。
    ××××
    ××××
    ××××
    ××××
    ××××
    ××××
    ××××
    ××××

プログラムの作成

  1. t[16][16] が魔方陣の最大サイズで、16*16の魔方陣まで作成できます。
    NUM が魔方陣のサイズで、4は4*4の魔方陣です。
        HINSTANCE   g_hInst;
        HWND        g_hWnd;
        USHORT      t[16][16];
        int         NUM= 4;
        
  2. WM_CREATE: では、NUM に設定されている魔方陣を作成します。
            case WM_CREATE:
                SetNumber(NUM);
                break;
        
  3. WM_PAINT: では、現在作成されている NUM の魔方陣を表示します。
            case WM_PAINT:
                hdc= BeginPaint (hWnd, &ps);
                Disp(hdc,NUM);
                EndPaint(hWnd, &ps);
                break;
        
  4. WM_COMMAND: でメニューから選択された奇数の魔方陣を作成して t[16][16] に格納します。
            case WM_COMMAND:
                switch(LOWORD(wParam))
                {   case IDM_4:  NUM= 4;    break;
                    case IDM_8:  NUM= 8;    break;
                    case IDM_12: NUM= 12;   break;
                    case IDM_16: NUM= 16;   break;
                    case IDM_EXIT:
                        SendMessage(hWnd,WM_CLOSE,0,0L);
                        return 0L;
                }
                SetNumber(NUM);
                InvalidateRect(hWnd,NULL,TRUE);
                break;
        
  5. 魔方陣を表示する Disp() 関数です。
        void  Disp(HDC hdc, int n)
        {   int     x,y;
            char    str[4];
    
            for(y=0; y<n; y++)
                for(x=0; x<n; x++)
                {   wsprintf(str,"%3d", t[y][x]);
                    TextOut(hdc,x*50+36,y*30+30,str,3);
                }
            for(x=0; x<n+1; x++)
            {   MoveToEx(hdc,x*50+20,20,NULL);
                LineTo(hdc,x*50+20,n*30+20);
                MoveToEx(hdc,20,x*30+20,NULL);
                LineTo(hdc,n*50+20,x*30+20);
            }
        }
        

【演習】

  1. NUM で指示された偶数の魔方陣を作成する関数 SetNumber(int n) を作成してプログラムを完成させて下さい。
    対角要素の判定は条件を組み合わせて判定します。
    例えば、0,0 から右下に伸びる対角要素は y==x の要領で判定して下さい。
  2. メニューで選択した偶数方陣を表示できるようにプログラムして下さい。
  3. この方法では全ての偶数の魔方陣を表示できるとは限りません。
    6方陣や10方陣を作成するアルゴリズムを考えてみて下さい。

魔方陣が成立する「縦、横の合計」は、次の式で計算することができます。
n を魔方陣のサイズとします。
配列 t[n][n] に1~(n*n+1) の値が格納されることになります。
縦(横)の合計= (n*n+1)*n/2; となります。
魔方陣 合計
(3*3+1)*3/2 15
(4*4+1)*4/2 34
(5*5+1)*5/2 65
(6*6+1)*6/2 111
(7*7+1)*7/2 175
(8*8+1)*8/2 260
12 (12*12+1)*12/2 870
16 (16*16+1)*16/2 2056

魔方陣の研究家として知られる秋田県雄勝郡稲川町大館、会社社長呵部楽方さん(55)が、
インドで1356年に書かれた魔方陣の本のコピーを手に入れ、このほど東京で開かれた
日本数学史学会の談話会で「インドの研究の水準は中国、イスラムに肩を並べるものだった」と紹介した。
魔方陣というのは、四角のマス目の中に数字を並ベ、縦、横、斜めのいずれに数字を足し合わせても
合計が等しくなるように配列したもの。
阿部さんがインドの知人を通して入手したのは、これまでその詳しい内容が明らかでなかった
ナーラーヤナ・パンディタという人の書いた本。
阿部さんによると、パンディタは最大十四方陣までの作り方を知っていたほか、図のような完全四方陣
(対角線からはずれた斜め方向でも、四つの数字の和は等しくなる)も作っていた。
また、六方陣の作り方の一つとして、1-36の数字を千鳥足並べにした後、数字を順ぐりに交換して
魔法陣を完成させる珍しい方法も考え出していた。

超初心者のプログラム入門(Win32API C++)