プログラミング
 Computer >> コンピューター >  >> プログラミング >> プログラミング

魔方陣(マジックスクエア)とは?生成ルールとC++実装方法をわかりやすく解説

魔方陣(マジックスクエア)とは

魔方陣(まほうじん)とは、正方行列の一種で、各行・各列・両対角線の要素の合計がすべて同じ値になるように数を配置したものです。ただし、その次数(行列のサイズ)は奇数である必要があります。

例えば、5×5の魔方陣は以下のようになります。

魔方陣(マジックスクエア)とは?生成ルールとC++実装方法をわかりやすく解説

各行・各列・各対角線の合計は、次の公式を使って求めることができます。

合計 = n(n² + 1) / 2

例えば n = 5 の場合、5 × (25 + 1) / 2 = 65 となり、実際に上記の魔方陣ではどの行・列・対角線の合計も 65 になっていることが確認できます。

魔方陣の構築ルール

奇数次の魔方陣は、以下の手順に従うことで機械的に作成できます。

  • 行列の最初の行の中央の列からスタートし、そこに「1」を配置します。以降は常に左上方向へ移動して次の数字を置いていきます。
  • 移動先の行が範囲外に出た場合は、列を一つ左に移動し、数字を最終行に配置してから、再度左上方向へ進みます。
  • 移動先の列が範囲外に出た場合は、行を一つ上に移動し、数字を最終列に配置してから、再度左上方向へ進みます。
  • 移動先の左上のマスがすでに埋まっている場合、または行と列の両方が範囲外の場合は、直前に配置した数字の真下に次の数字を配置します。

入力と出力の例

入力:
行列の次数 5

出力:
15  8  1 24 17
16 14  7  5 23
22 20 13  6  4
 3 21 19 12 10
 9  2 25 18 11

アルゴリズム

魔方陣を生成する関数 createSquare(mat, r, c) の擬似コードは以下の通りです。

入力: 行列 mat、行数 r、列数 c
出力: 完成した魔方陣

Begin
    count := 1
    mat の全要素を 0 で初期化
    range := r * c
    i := 0
    j := c / 2
    mat[i, j] := count   // 最上行の中央に「1」を配置

    while count < range, do
        count を 1 増やす
        if i と j の両方が範囲外ならば
            i を 1 増やす
        else if i のみ範囲外ならば
            i := c - 1
            j を 1 減らす
        else if j のみ範囲外ならば
            j := c - 1
            i を 1 減らす
        else if (i, j) が範囲内かつ mat[i, j] ≠ 0 ならば
            i を 1 増やす
        else
            i と j をそれぞれ 1 減らす
        mat[i, j] := count
    done
    行列 mat を表示
End

C++による実装例

上記のアルゴリズムをC++で実装したコードがこちらです。

#include<iostream>
#include<iomanip>
using namespace std;

void createSquare(int **array, int r, int c) {
    int i, j, count = 1, range;
    for(i = 0; i<r; i++)
        for(j = 0; j<c; j++)
            array[i][j] = 0;      // 全要素を0で初期化

    range = r*c;
    i = 0;
    j = c/2;
    array[i][j] = count;

    while(count < range) {
        count++;
        if((i-1) < 0 && (j-1) < 0)     // 行と列の両方が範囲外の場合
            i++;
        else if((i-1) <0) {            // 行のみ範囲外 → 最終行へ移動し、jを減らす
            i = r-1;
            j--;
        }else if((j-1) < 0) {          // 列のみ範囲外 → 最終列へ移動し、iを減らす
            j = c-1;
            i--;
        }else if(array[i-1][j-1] != 0) // 左上が埋まっている場合は次の行へ
            i++;
        else{
            i--;
            j--;
        }
        array[i][j] = count;
    }

    // 魔方陣の表示
    for(i = 0; i<r; i++) {
        for(j = 0; j<c; j++)
            cout <<setw(3) << array[i][j];
        cout << endl;
    }
}

main() {
    int** matrix;
    int row, col;
    cout << "Enter the order(odd) of square matrix :";
    cin >> row;
    col = row;

    matrix = new int*[row];

    for(int i = 0; i<row; i++) {
        matrix[i] = new int[col];
    }
    createSquare(matrix, row, col);
}

実行結果

Enter the order(odd) of square matrix :5
 15  8  1 24 17
 16 14  7  5 23
 22 20 13  6  4
  3 21 19 12 10
  9  2 25 18 11

まとめ

魔方陣は、古代から数学的な興味の対象とされてきた美しい数の配置パターンです。奇数次の魔方陣であれば、「右上方向への移動」と「衝突時の下への移動」というシンプルなルールだけで自動生成できる点が特徴です。このアルゴリズムは計算量が O(n²) と効率的であり、プログラミング学習における二次元配列操作の練習題材としても最適です。

  1. 正方形の中にある葉の形の面積を求める方法

    ここでは、正方形ABCDの内部に存在する、下図のような葉の形(リーフ形状)の面積を求める方法を解説します。正方形の各辺の長さは「a」とします。この葉の形は、互いに等しい2つの部分で構成されています。各部分の面積を p とすると、次のようになります。したがって、葉全体の面積は 2p となります。計算式葉の形は半径 a の円弧によって囲まれた領域から成り立っているため、その面積は以下の公式で求められます。葉の面積 = a2 × (π/2 − 1)実装例(C++)#include <iostream> using namespace std; float leafArea(float a

  2. C++で数独を解く!バックトラッキングによる数独ソルバーの実装方法

    9×9のマス目に並んだ数字のパズル「数独(Sudoku)」を、プログラムで自動的に解く方法を解説します。数独は9×9の数字グリッドから成り、その全体がさらに3×3のブロック(ボックス)に分割されているのが特徴です。数独を解くための基本ルール使用するのは1から9までの数字のみです。同じ行、同じ列、同じ3×3ブロック内に、同じ数字を重複させて配置することはできません。バックトラッキングによる解法ここでは「バックトラッキング」という手法を用いて数独を解きます。バックトラッキングとは、空いているセルに仮に数字を入れてみて、その配置が正しいかどうかを検証する方法です。もし配置が不正であれば別の数字を試し