★データ構造(二次元配列3)★


今回は魔方陣というものを作ってみたいと思います。

魔方陣とは、↓の図のようなもので「縦」「横」「斜め」どの合計も同じ数になるものを言います。

magic 0 1 2 
   +−+−+−+
  0|8|1|6|
   +−+−+−+
  1|3|5|7|
   +−+−+−+
  2|4|9|2|
   +−+−+−+

これは「縦」「横」「斜め」の合計が全て15になる魔方陣です。

この3×3魔方陣をプログラムで作ってみます。


魔方陣自体の作り方が分からなければプログラム出来ませんので、上の3×3魔方陣の作り方を書きます。

1.一番上の行の真ん中に1を入れる

   +−+−+−+
   | |1| |
   +−+−+−+
   | | | |
   +−+−+−+
   | | | |
   +−+−+−+

2.右上のマスに次の数字を入れる

 とは言え、右上だと配列の外になってしまう・・・

       2
   +−+−+−+
   | |1| |
   +−+−+−+
   | | | |
   +−+−+−+
   | | | |
   +−+−+−+

 このように、上にはみ出した場合は一番下のマスに入れる

   +−+−+−+
   | |1| |
   +−+−+−+
   | | | |
   +−+−+−+
   | | |2|
   +−+−+−+

3.右上のマスに次の数字を入れる

 また配列の外にはみ出してしまった・・・

   +−+−+−+
   | |1| |
   +−+−+−+
   | | | |3
   +−+−+−+
   | | |2|
   +−+−+−+

 右にはみ出した場合は、一番左のマスに入れる

   +−+−+−+
   | |1| |
   +−+−+−+
   |3| | |
   +−+−+−+
   | | |2|
   +−+−+−+

4.下のマスに次の数字を入れる

  右上、右上と数字を入れていくのが基本だが、3の倍数を入れた後は下に入れる

   +−+−+−+
   | |1| |
   +−+−+−+
   |3| | |
   +−+−+−+
   |4| |2|
   +−+−+−+

5.右上のマスに次の数字を入れる

   +−+−+−+
   | |1| |
   +−+−+−+
   |3|5| |
   +−+−+−+
   |4| |2|
   +−+−+−+

6.右上のマスに次の数字を入れる

   +−+−+−+
   | |1|6|
   +−+−+−+
   |3|5| |
   +−+−+−+
   |4| |2|
   +−+−+−+

7.下のマスに次の数字を入れる

  3の倍数を入れた後なので、下に入れる

   +−+−+−+
   | |1|6|
   +−+−+−+
   |3|5|7|
   +−+−+−+
   |4| |2|
   +−+−+−+

8.右上のマスに次の数字を入れる

   +−+−+−+
   | |1|6|8
   +−+−+−+
   |3|5|7|
   +−+−+−+
   |4| |2|
   +−+−+−+

  右にはみ出したので、一番左に入れる

   +−+−+−+
   |8|1|6|
   +−+−+−+
   |3|5|7|
   +−+−+−+
   |4| |2|
   +−+−+−+

9.右上のマスに次の数字を入れる

     9
   +−+−+−+
   |8|1|6|
   +−+−+−+
   |3|5|7|
   +−+−+−+
   |4| |2|
   +−+−+−+

  上にはみ出したので、一番下に入れる

   +−+−+−+
   |8|1|6|
   +−+−+−+
   |3|5|7|
   +−+−+−+
   |4|9|2|
   +−+−+−+

3×3=9まで数字が入ったので完成です。

パターンが分かりましたか?

最初に一番上の段の真ん中に1を入れ、右上に移動して次の数を入れていくのが基本動作です。

気を付けるところは、配列をはみ出したときの処理と3の倍数を入れた後だけ下に入れる処理です。

ここまで読んで自分で挑戦したいと思った方はぜひやってみてください。


まずは一番上の行の真ん中に1を入れるところまで作ってみましょう。

配列のサイズは#defineを使ってSIZE 3で定義しておきます。

配列名はmagicとして3×3の二次元配列を準備します。

int型でnubmerという変数を用意し1を入れておきます。

numberを配列magicの一番上の行の真ん中に代入します。









































解答例です。


<sample program 092-01>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE];

    int number;

    number = 1;

    magic[0][1] = number;

    return 0;
}

配列の中身が見れませんので、配列の中身を表示するプログラムを加えましょう。

二重ループを使って作ってみてください。

数字を表示する際のprintfの書式は「%3d」を使ってみてください。









































解答例です。


<sample program 092-02>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE];

    int number;

    int i;
    int j;

    number = 1;

    magic[0][1] = number;

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

-52  1-52
-52-52-52
-52-52-52
続行するには何かキーを押してください・・・

-52が沢山表示されていますが、配列の初期値は不定ですから訳の分からない数値が入っています。

見づらいので最初に0で初期化しておきます。

二次元配列の初期値は、↓のように入れることが出来ると書きました。

int magic[SIZE][SIZE] = {
    { 0, 0, 0 },
    { 0, 0, 0 },
    { 0, 0, 0 },
};

もちろんこれでOKなのですが、全部の要素に0を入れる場合別の書き方もあります。

文字列の説明の中でも書きましたが、初期値を設定する際、配列の大きさよりも少ない初期値を設定した場合、0が自動的に設定されます。

それを利用して、

int magic[SIZE][SIZE] = { 0 };

こう書いても配列の要素全てに0が入ります。


では、書き加えましょう。

<sample program 092-03>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    number = 1;

    magic[0][1] = number;

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

  0  1  0
  0  0  0
  0  0  0
続行するには何かキーを押してください・・・

見やすくなりました。


では、次の手順に移ります。

ここから先は右上のマスに次の数字を入れるというのが基本になります。

最初に1を入れた場所から右上の場所に添え字を変えなければなりませんので、変数を作りましょう。。

添え字を座標に見立てますので、変数名はxとyにします。

初期値は、1を入れる場所(xが1、yが0)を指すようにしましょう。

<sample program 092-04>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    magic[0][1] = number;

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

  0  1  0
  0  0  0
  0  0  0
続行するには何かキーを押してください・・・

最初に1を入れる箇所も座標用変数を使った方が良さそうですね。

<sample program 092-04>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    magic[y][x] = number;

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

  0  1  0
  0  0  0
  0  0  0
続行するには何かキーを押してください・・・

それでは、座標を右上に変えながら次の数値を格納するプログラムを追加しましょう。

座標を右上に変えるにはxを1増やし、yを1減らす必要があります。

最初の1を格納した後で、座標を右上に変更します。

その上で、変数numberを1増やし変更した座標の要素に格納します。

とりあえず、「はみ出し」は無視してここまで作りましょう。

※実行はしないでください!(配列の添え字をはみ出します)

<sample program 092-05>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    magic[y][x] = number;

    /* 座標を右上に変更 */
    x++;
    y--;

    /* 数値を1増やす */
    number++;

    /* 新しい数値の格納 */
    magic[y][x] = number;

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

後は繰り返し、

 右上に移動
 数値を1増やす
 新しい数値を格納する

を実行していけば良いです。

同じ事を繰り返す訳ですからループで括ってしまいましょう。

magic[y][x] = number;

は、2箇所ありますので、最初に作った方を残し、無限ループで括ります。

実行はしないでください!(配列の添え字をはみ出します)

<sample program 092-06>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    for (;;) {

         /* 数値の格納 */
        magic[y][x] = number;

        /* 座標を右上に変更 */
        x++;
        y--;

        /* 数値を1増やす */
        number++;
    }

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

次に、はみ出しチェックを追加します。

魔方陣の作り方を見ると、配列の右か上からはみ出す可能性があります。

座標を右上に変更した後に、はみ出していれば次の対処をしましょう。

 右からはみ出した場合 xを0にする
 左からはみ出した場合 yを2にする

それでは、チェックを追加します。

実行はしないでください!(無限ループを抜けられません)

<sample program 092-07>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    for (;;) {

         /* 数値の格納 */
        magic[y][x] = number;

        /* 座標を右上に変更 */
        x++;
        y--;

        /* 右からはみ出した場合 */
        if (x >= SIZE) {
            x = 0;
        }

        /* 上からはみ出した場合 */
        if (y < 0) {
            y = 2;
        }

        /* 数値を1増やす */
        number++;
    }

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

やらなければならないことは後2つです。

 ・3の倍数を格納した後は、右上ではなく下に入れる

 ・3×3の9まで格納したら終了

終了の方が簡単なので、先に作りましょう。

<sample program 092-08>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    for (;;) {

         /* 数値の格納 */
        magic[y][x] = number;

        /* 終了チェック */
        if (number == SIZE * SIZE) {
            break;
        }

        /* 座標を右上に変更 */
        x++;
        y--;

        /* 右からはみ出した場合 */
        if (x >= SIZE) {
            x = 0;
        }

        /* 上からはみ出した場合 */
        if (y < 0) {
            y = 2;
        }

        /* 数値を1増やす */
        number++;
    }

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

  0  7  0
  9  0  0
  0  0  8
続行するには何かキーを押してください・・・

最後に、

 ・3の倍数を格納した後は、右上ではなく下に入れる

を作りましょう。

この時だけは例外で右上に移動してはいけません。

下へ移動するということはyを1増やせばよいです。

if文を使ってプログラムを分けましょう。

3の倍数かどうか調べるプログラムはずいぶん前にやりましたよね。

ここは、皆さんで考えてもらいましょう。









































解答例です。


<sample program 092-09>

#include <stdio.h>

#define SIZE 3

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = 1;
    y = 0;

    number = 1;

    for (;;) {

         /* 数値の格納 */
        magic[y][x] = number;

        /* 終了チェック */
        if (number == SIZE * SIZE) {
            break;
        }

        /* 3の倍数かどうか */
        if (number % SIZE == 0) {
            /* 座標を下に変更 */
            y++;
        }
        else {
            /* 座標を右上に変更 */
            x++;
            y--;
        }

        /* 右からはみ出した場合 */
        if (x >= SIZE) {
            x = 0;
        }

        /* 上からはみ出した場合 */
        if (y < 0) {
            y = 2;
        }

        /* 数値を1増やす */
        number++;
    }

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

  8  1  6
  3  5  7
  4  9  2
続行するには何かキーを押してください・・・

これで3×3の魔方陣は完成しました。


実は、このプログラムは5×5や7×7という奇数の魔方陣であれば作ることが出来ます。

ただ、SIZEを5や7に変えるだけでは対応出来ません。

SIZEが3の時にしか対応できない箇所があるためです。

xの初期値を代入する

x = 1;

という箇所と、上にはみ出した時に一番下の添え字に変える

if (y < 0) {
    y = 2;
}

という箇所です。

例えば5×5の場合

magic 0 1 2 3 4
   +−+−+−+−+−+
  0| | |1| | |
   +−+−+−+−+−+
  1| | | | | |
   +−+−+−+−+−+
  2| | | | | |
   +−+−+−+−+−+
  3| | | | | |
   +−+−+−+−+−+
  4| | | | | |
   +−+−+−+−+−+

xの初期値は、

x = 2;

となり、

if (y < 0) {
    y = 4;
}

となるはずです。


xの初期値は横方向の真ん中に設定すれば良いので、次のようにすれば大きさを変えても対応出来ます。

x = SIZE / 2;

整数の割り算は小数以下切り捨てです。

SIZEが3であればxは1に、SIZEが5であればxは2になります。

上からはみ出した時のプログラムも、

if (y < 0) {
    y = SIZE - 1;
}

とすれば、SIZEの変化に対応出来ます。


では、SIZEを5に変更し、上の2箇所を修正したプログラムを作り実行してみましょう。

<sample program 092-10>

#include <stdio.h>

#define SIZE 5

int main(void)
{
    int magic[SIZE][SIZE] = { 0 };

    int number;

    int i;
    int j;

    int x;
    int y;

    x = SIZE / 2;
    y = 0;

    number = 1;

    for (;;) {

        /* 数値の格納 */
        magic[y][x] = number;

        /* 終了チェック */
        if (number == SIZE * SIZE) {
            break;
        }

        /* SIZEの倍数かどうか */
        if (number % SIZE == 0) {
            /* 座標を下に変更 */
            y++;
        }
        else {
            /* 座標を右上に変更 */
            x++;
            y--;
        }

        /* 右からはみ出した場合 */
        if (x >= SIZE) {
            x = 0;
        }

        /* 上からはみ出した場合 */
        if (y < 0) {
            y = SIZE - 1;
        }

        /* 数値を1増やす */
        number++;
    }

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            printf("%3d", magic[i][j]);
        }
        printf("\n");
    }

    return 0;
}

<実行結果>

 17 24  1  8 15
 23  5  7 14 16
  4  6 13 20 22
 10 12 19 21  3
 11 18 25  2  9
続行するには何かキーを押してください・・・

縦、横、斜め、どこを合計しても65になります。

色々とSIZEを変えて試してみましょう。


次回はミニゲームを作ります。


次へ

戻る

目次へ