マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第6章 6次魔方陣・8次魔方陣の作成

⁂私が2022を使っている間に2026の設定が変わってしまっています。
がUTF‑8になっている場合にはShift-JISに変更して
エンコード付きで保存してください。⁂


第8話 行合計・列合計・対角線合計を計算させて表示させる


1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
19 20 21 22 23 24
25 26 27 28 29 30
31 32 33 34 35 36


111
36 32 4 3 5 31 111
12 29 27 10 26 7 111
19 17 22 21 14 18 111
13 20 16 15 23 24 111
25 11 9 28 8 30 111
6 2 33 34 35 1 111

111 111 111 111 111 111 111

を実現するコード例
#include<iostream>//インクルードファイルiostreamの読み込み

#include<conio.h>//while(!_kbhit());を使うためのお呪い

#include<string> //文字列変数を使えるようにするために組み込む

#include <iomanip> //setprecisionを使えるように組み込む

#include <cmath>//powなどを使うときに必要

#include <ctime>//time()(←現時刻発生する関数)を使うために必要

using namespace std;//coutを使うときに必要なお呪い

void 魔方陣();//横と縦の2方向を持つ2次元for文体験

const int n = 6;//これからは具体的な数字を使わずにnとする。左の6を8などに変更すれば8次魔方陣に対応する
//n = 6 の一か所だけ具体的な数字を使い、他ではすべて n を使ってください。

int main() {//私は社長だ。

  魔方陣();

  while (!_kbhit());//待機させるための命令

  return(0);//int main()終わるためのお呪い

}

void 魔方陣() {

  int a[n][n];

  for (int i = 0; i < n; i++) {

    for (int j = 0; j < n; j++) {

      a[i][j] = n * i + j + 1;//自然配列を生成してaに収納する

    }

  }

  for (int i = 0; i < n; i++) {

    for (int j = 0; j < n; j++) {

      //自然配列に体裁を整えて表示する。

      if (a[i][j] < 10)cout << " ";

      cout << a[i][j]<<" ";

    }

    cout << endl;

  }

  cout << endl;

  int 受け皿;//a[i][j]などのデータを一時保存する

   //対角線部分を交換する

  for (int i = 0; i < n / 2; i++) {
//2026年2月17日修正 修正内容(3 → n / 3) 修正理由具体的数字3が入ってしまっている

    受け皿 = a[i][i];
    //a[i][i]のデータがa[n - 1 - i][n - 1 - i]のデータに上書きされる前に受け皿に保存

    a[i][i] = a[n - 1 - i][n - 1 - i];
    //a[i][i]のデータをa[n - 1 - i][n - 1 - i]のデータで上書き

    a[n - 1 - i][n - 1 - i] = 受け皿;
    //a[n - 1 - i][n - 1 - i]のデータをa[i][i]の元データで保存

  }

  //対角線部分の交換終了


  //逆対角線部分を交換する

  for (int i = 0; i < n / 2; i++) {
//2026年2月17日修正 修正内容(3 → n / 2) 修正理由 具体的数字3が入ってしまっている

    受け皿 = a[i][n - 1 - i];
    //a[i][n - 1 - i]のデータがa[n - 1 - i][i]のデータに上書きされる前に受け皿に保存

    a[i][n - 1 - i] = a[n - 1 - i][i];
    //a[i][n - 1 - i]のデータをa[n - 1 - i][i]のデータで上書き

    a[n - 1 - i][i] = 受け皿;
    //a[n - 1 - i][i]のデータをa[i][n - 1 - i]の元データで保存

  }

  //逆対角線部分の交換終了

   //明るい紫色の部分の交換

  for (int i = 0; i < n / 2; i++) {
//2026年2月17日修正 修正内容(3→ n / 2) 修正理由 具体的数字3が入ってしまっている

    受け皿 = a[i][(2 + i) % (n /2)];
//2026年2月17日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入ってい待っている
    //a[i][(2 + i) % (n /2)]のデータがa[i][n - 1 - ((2 + i) % (n /2))]
    // のデータ上書きされる前に受け皿に保存

    a[i][(2 + i) % (n /2)] = a[i][n - 1 - ((2 + i) % (n /2))];
      //2026年2月17日修正 修正内容(3→ (n / 2)) 修正理由 具体的な数字3が入っている
    //a[i][(2 + i) % (n /2)]のデータをa[i][n - 1 - ((2 + i) % (n /2))]のデータで上書き
//2026年2月17日修正 修正内容(3→ (n / 2))

    a[i][n - 1 - ((2 + i) % (n /2))] = 受け皿;
    //a[i][n - 1 - ((2 + i) % (n /2))]のデータをa[i][(2 + i) % (n /2)]の元データで上書き
//2026年2月17日修正 修正内容(3→ (n / 2))

  }

  //明るい紫色の部分の交換終了


  //緑の部分の交換終了

  for (int i = 0; i < n / 2; i++) {
//2026年2月17日修正 修正内容(3→ n / 2)修正理由 具体的数字3が入ってしまっている

    受け皿 = a[i][(1 + i) % (n /2)];
//2026年2月17日修正 修正内容(3→ (n / 2))修正理由 具体的数字3が入ってしまっている
    //a[i][(1 + i) % (n /2)]のデータがa[n - 1 - i][(1 + i) % (n /2)]
    // のデータに上書きされる前に受け皿に保存

    a[i][(1 + i) % (n /2)] = a[n - 1 - i][(1 + i) % (n /2)];

    //2026年2月15日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入ってしまっている

    //a[i][(1 + i) % (n /2)]のデータをa[n - 1 - i][(1 + i) % (n /2)]のデータで上書き

    a[n - 1 - i][(1 + i) % (n /2)] = 受け皿;
//2026年2月17日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入ってしまっている
    //a[n - 1 - i][(1 + i) % (n /2)]のデータをa[i][(1 + i) % (n /2)]の元データで上書き

  }

  //緑の部分の交換終了

  int 行合計[n] = { 0,0,0,0,0,0 };//行合計の定義と初期化

  int 列合計[n] = { 0,0,0,0,0,0 };//列合計の定義と初期化

  int 対角線合計[2] = { 0,0 };//対角線合計の定義と初期化

  //各行合計の計算
  for (int i = 0; i < n; i++) {

     for (int j = 0; j < n; j++) {

       行合計[i] = 行合計[i] + a[i][j];

     }

  }

  //各列合計の計算
  for (int i = 0; i < n; i++) {

    for (int j = 0; j < n; j++) {

      列合計[i] = 列合計[i] + a[i][j];

    }

  }

  
//各列合計の計算
  for (int i = 0; i < n; i++) {

    対角線合計[0] = 対角線合計[0] + a[i][i];

    対角線合計[1] = 対角線合計[1] + a[i][n - 1 - i];

  }

  //6次魔方陣 + 各合計の表示

  cout << endl;

  for (int i = 0; i < n; i++) {

    cout << " ";

  }

  cout << " " << 対角線合計[1] << endl;

  for (int i = 0; i < n; i++) {

    for (int j = 0; j < n; j++) {

      if (a[i][j] < 10)cout << " ";

      cout << " " << a[i][j] << " ";

    }

    cout << " " << 行合計[i];

    cout << endl;

  }

  cout << endl;

  for (int i = 0; i < n; i++) {

    cout << 列合計[i] << " ";

  }

  cout << " " << 対角線合計[0] << " ";

  //6次魔方陣 + 各合計の表示終了


}


今回は自然配列を対角線部分・明るい紫部分・緑部分を対称移動させただけですから、

1~36までの数字が重複なく漏れなくそろっていることは自明ですが、

奇数次魔方陣の生成には重複検査が必須ですので、

小さい順に並び替えて1~36までの数字が重複なく漏れなくそろっていることを証明してみたいと思います。

ただし、並び替えという取り組みは初心者には難しい課題ですので、

簡単な例題をやっていただいてから、

魔方陣の各数字を小さい順に並べ返すという課題に取り組んでいただきたいと思います。

例題

8 3 4 9 1 5 2 7 6

を小さい順に並べ直して

1 2 3 4 5 6 7 8 9


という課題に取り組んでいただきたいともいますが、

初心者には難しい課題ですから、

段階を踏んで実現していきます。

課題1

8 3 4 9 1 5 2 7 6

の最小値を求めて表示させてください。

条件は1次元配列を用意して

8 3 4 9 1 5 2 7 6

を収納してから表示させ

最小値を表示してください。

新しいプロジェクトの作成で『並び替え』という名称にして取り組んでください。

やり方を忘れた方は第6章第2話などを参照してください。


8 3 4 9 1 5 2 7 6 すなわち
a[0] = 8
a[1] = 3
a[2] = 4
a[3] = 9
a[4] = 1
a[5] = 5
a[6] = 2
a[7] = 7
a[8] = 6

最小値はa[4] = 1 です。

ヒントは最小値を定義してあり得ない数字で初期化しておきます。

たとえば、

  最小値 = 100;

などです。

そして、最小値とa[i] などと比較して最小値より小さければ、

最小値をa[i] に書き換えそのときのiも記録しておくのです。

ですからfor文の前に

  int ik;//最小値になるときのiを収納する変数

を定義しておく必要があります。

a[0] = 8 の表示は今までを振り返って考えてください。

ヒントは
  
  cout << "私は" << "、" << "天才です。" << endl;

とすれば

私は、天才です。

と表示されることです。








第6章第7話へ 第6章第9話へ

本講義トップへ