アルゴリズム

C言語による魔方陣生成アルゴリズム:奇数階と偶数階の実装例について解説

この記事では、C言語を用いて魔方陣を生成するアルゴリズムについて解説します。

奇数階の場合は、従来のシアム法に基づいた手法を紹介し、偶数階に対応するための実装例も合わせて説明します。

具体的なコード例をもとに、魔方陣生成の基本的な考え方と処理の流れを理解しやすくまとめています。

奇数階魔方陣の生成アルゴリズムについて解説

アルゴリズムの基本手順

初期配置と移動ルールの解説

奇数階の魔方陣を生成する基本アルゴリズムは、下記の手順で進行します。

まず、魔方陣の最初の数字である1を、上段中央に配置します。

次に、次の数字を配置する際は、現在のセルから上に一つ、右に一つ移動します。

なお、移動先が行列外になる場合は、行または列が反対側に折り返すように設定されます。

もし移動先がすでに埋まっている場合は、代わりに直前のセルの下に配置するルールとなります。

たとえば、次のように処理が進みます。

  • 初期値はセル位置(0,n/2)
  • 次のセルの位置は (i1,j+1) ですが、mod nを用いて行と列の範囲内に収めます。
  • すでに埋まっている場合には、(i+1,j) に移動します。

この手法は、セルのインデックスのラップアラウンド処理により、どのセルにも必ず配置が可能となるのが特徴です。

計算処理の流れ

奇数階魔方陣の生成では、全体でn×n個のセルに対して、1からn2までの連番を順次配置します。

以下の流れでループが実行されます。

  • 変数nに魔方陣の階数を保持します。
  • 変数numを1からn2までインクリメントしながら、各セルに値を設定します。
  • 現在のセルの位置を更新する際に、座標(i,j)は下記のように更新され、境界チェックにはモジュロ演算が利用されます。

inew=(i1+n)modn,jnew=(j+1)modn

  • すでに値が配置されている場合は、(i+1,j)に変更して再配置します。

このアルゴリズムのキーポイントは、モジュロ演算を用いることにより境界の移動処理をシンプルに行える点です。

実装例のコード解説

配列の初期化と入力処理

以下のサンプルコードは、奇数階魔方陣のための配列初期化とユーザーからの入力処理を行います。

ユーザーが入力した魔方陣の階数が奇数であるかをチェックする部分も含んでいます。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 99  // 魔方陣の最大サイズ
int main(void) {
    int n;
    int magic[MAX_SIZE][MAX_SIZE] = {0};  // 配列の初期化
    // ユーザーから魔方陣の階数を入力
    printf("奇数階魔方陣の階数を入力してください(例:3,5,7など):");
    if (scanf("%d", &n) != 1 || n % 2 == 0 || n > MAX_SIZE) {
        printf("入力エラー:奇数の正の整数を入力してください。\n");
        return 1;
    }
    // 配列を0で初期化(必要に応じて)
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            magic[i][j] = 0;
        }
    }
    // 以下、魔方陣生成のループ処理へ移る
    // ...
    return 0;
}
奇数階魔方陣の階数を入力してください(例:3,5,7など):3

ループ処理と位置更新の実装

次に、魔方陣に数字を配置するためのループ処理と位置更新の実装例です。

上記の初期化部分に続けて、1からn2までの数字を配置する処理を示します。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 99  // 魔方陣の最大サイズ
int main(void) {
    int n;
    int magic[MAX_SIZE][MAX_SIZE] = {0};  // 配列の初期化
    printf("奇数階魔方陣の階数を入力してください(例:3,5,7など):");
    if (scanf("%d", &n) != 1 || n % 2 == 0 || n > MAX_SIZE) {
        printf("入力エラー:奇数の正の整数を入力してください。\n");
        return 1;
    }
    // 初期位置の設定
    int i = 0;
    int j = n / 2;
    magic[i][j] = 1;
    // 2からn*nまでループ処理
    for (int num = 2; num <= n * n; num++) {
        int new_i = (i - 1 + n) % n;  // モジュロ演算で上に移動
        int new_j = (j + 1) % n;        // モジュロ演算で右に移動
        // 移動先がすでに埋まっている場合は、下に移動
        if (magic[new_i][new_j] != 0) {
            i = (i + 1) % n;
        } else {
            i = new_i;
            j = new_j;
        }
        magic[i][j] = num;
    }
    // 結果の出力
    printf("生成された奇数階魔方陣:\n");
    for (int r = 0; r < n; r++) {
        for (int c = 0; c < n; c++) {
            printf("%3d ", magic[r][c]);
        }
        printf("\n");
    }
    return 0;
}
奇数階魔方陣の階数を入力してください(例:3,5,7など):3
生成された奇数階魔方陣:
 8  1  6
 3  5  7
 4  9  2

偶数階魔方陣の生成アルゴリズムについて解説

アルゴリズムの基本手順

特殊な配置と反転ルールの解説

偶数階の魔方陣は、その種類によってアルゴリズムが異なりますが、ここでは特に4で割り切れる「両偶階」の魔方陣の生成方法に注目します。

基本手順は以下となります。

  1. 配列に1からn2までの数字を順番に埋めます。
  2. 一部の位置(たとえば、対角線上や特定のブロックに含まれるセル)の数字を入れ替えます。この入れ替えは、セルのインデックスに基づく条件式

(imod4==jmod4)または((i+j)mod4==3)

などに該当する場合に行います。

このようにして、全体の合計が各行、各列、さらには対角線でも同じ数になる魔方陣が実現されます。

配列の変換処理の流れ

初期状態で順に数字を配置した後、配置済みの配列に対して指定したルールに従い要素の交換処理を行います。

具体的には、下記の流れで変換処理が実施されます。

  • 2重ループで各セルを走査します。
  • 指定された条件を満たすセルでは、セルの値を

n2+1現在の値

に更新します。

  • この反転操作により、魔方陣の各列および各行の和が一定となる性質が得られます。

実装例のコード解説

データ構造と制御文の活用

偶数階魔方陣(両偶階)の生成例として、以下のサンプルコードを紹介します。

コードは、まず配列に連番を埋め、その後、条件に基づいて値を入れ替える処理を実装しています。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100  // 偶数階魔方陣の最大サイズ
int main(void) {
    int n;
    int magic[MAX_SIZE][MAX_SIZE] = {0};
    // ユーザーから魔方陣の階数を入力(両偶階の場合は4の倍数)
    printf("両偶階魔方陣の階数を入力してください(例:4,8,12など):");
    if (scanf("%d", &n) != 1 || n % 4 != 0 || n > MAX_SIZE) {
        printf("入力エラー:4の倍数の正の整数を入力してください。\n");
        return 1;
    }
    // 配列に1からn*nまで順に代入
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            magic[i][j] = i * n + j + 1;
        }
    }
    // 特定条件において値の反転処理
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            // (i, j)がダイアゴナルまたは特定のブロックに含まれる場合に反転
            if ((i % 4 == j % 4) || ((i + j) % 4 == 3)) {
                magic[i][j] = n * n + 1 - magic[i][j];
            }
        }
    }
    // 結果の出力
    printf("生成された両偶階魔方陣:\n");
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            printf("%4d ", magic[i][j]);
        }
        printf("\n");
    }
    return 0;
}
両偶階魔方陣の階数を入力してください(例:4,8,12など):4
生成された両偶階魔方陣:
 16   2   3  13
  5  11  10   8
  9   7   6  12
  4  14  15   1

出力処理の実装ポイント

上記のサンプルコードでは、各セルの出力時にprintf関数のフォーマット指定子%4dを利用することで、各数字の幅を揃えて表示しています。

これにより、魔方陣全体が整然とした形式で出力され、視認性が向上します。

また、改行を適切に入れることで、各行ごとに区切られた出力となるよう配慮しています。

C言語実装における技術的ポイント

メモリ管理と配列操作の工夫

エラーチェックと例外処理の実装

プログラムでは、ユーザーが入力する値の妥当性をチェックする工程があります。

奇数階、あるいは両偶階の魔方陣の生成においては、以下の点に注意しています。

  • ユーザー入力が正しい整数であるかどうかをscanfの戻り値で確認すること。
  • 奇数階の場合は入力値が奇数か、両偶階の場合は4の倍数かを条件でチェックし、不正な場合はエラーメッセージを出力してプログラムを終了する。
  • 固定サイズの配列を使用するため、入力される階数が配列の上限を超えないように注意する。

こうしたエラーチェックと例外処理は、プログラムの健全性を保ち、不正な入力や予期せぬ動作を防ぐための基本的な対策となります。

コードの可読性と保守性向上のポイント

コメントとコード整理の方法

C言語による魔方陣生成プログラムは、各処理の役割を明確に示すために、適切なコメントを付加することが重要です。

以下に、可読性と保守性を高めるためのポイントを示します。

  • 各主要処理(例:入力処理、初期配置、ループ処理、出力処理)ごとに、コメントで概要を記述する。
  • 数式や更新処理の部分には、どのようなルールに則っているかを簡潔に説明するコメントを記載する。
  • コードを関数ごとに分割し、各関数がひとつの役割に専念できるように整理する。たとえば、魔方陣の生成ロジックを別関数に切り出すことで、読み手が全体の流れを把握しやすくする。
  • 読みやすい変数名(例:magicrowcol)や定数(例:MAX_SIZE)を使用する。

これらの工夫により、プログラムの見通しが良くなり、後の改修や拡張が容易になります。

まとめ

本記事では、C言語を用いて奇数階および偶数階(両偶階)の魔方陣を生成するアルゴリズムを解説しています。

奇数階は、上段中央から開始し、モジュロ演算で移動先を決定しながら値を配置する方法を紹介。

偶数階は、連番埋めと反転操作を組み合わせる手法を示しています。

さらに、配列の初期化、エラーチェック、出力整形などの実装ポイントも取り上げ、理解しやすいサンプルコードと共に技術的な工夫が説明されています。

関連記事

Back to top button
目次へ