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

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


第10話 小さい順での並び替え


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 です。

1 2 3 4 5 6 7 8 9

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

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

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

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

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

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

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

const int n = 9;//具体的な数字でコーティングするのではなく必ずnでコーティングしてください。

void 並び替え();

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

  並び替え();

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

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

}

void 並び替え() {

  int a[9] = { 8,3,4,9,1,5,2,7,6 };//aの定義と初期化

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

    cout << a[i] << " ";//最初の数字列を表示

  }

  cout << "すなわち" << endl;

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

    cout << "a["<<i<<"] = "<<a[i] << endl;

  }

  int 最小値 = 100;

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

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

    if (a[i] < 最小値) {

      最小値 = a[i];

      ik = i;

    }

  }

  cout << endl;

  cout << "最小値はa[" << ik << "] = " << a[ik] << " です。" << endl;

  //小さい順への並び替え
  for (int i = 0; i < n; i++) {

    最小値 = 100;//あり得ない数字に初期化

    int jk;//jを記録する変数

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

      if (a[j] < 最小値) {

        最小値 = a[j];

       jk = j;

      }

   }

   int 受け皿;

   受け皿 = a[i];

   a[i] = a[jk];

   a[jk] = 受け皿;

  }
  //表示
  cout << endl;

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

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

  }

  cout << endl;

}

さて、小さい順に並び替えましょう。

その作業に入る前に、下のコードをコピペしてください。

今まで変数や配列を長めの漢字で表してきましたが、

初心者には短い変数名や配列名の方がわかりやすいのいう判断をしまして、

そのように変更しました。

#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 u;//a[i][j]などのデータを一時保存する

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

  for (int i = 0; i < n / 2; i++) {
//2026年2月16日修正 修正理由具体的な数字3が入ってしまっていた。

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

    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] = u;
    //a[n - 1 - i][n - 1 - i]のデータをa[i][i]の元データで保存

  }

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


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

  for (int i = 0; i < n / 2; i++) {
//2026年2月16日修正 修正理由具体的な数字3が入ってしまっていた。

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

    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] = u;
    //a[n - 1 - i][i]のデータをa[i][n - 1 - i]の元データで保存

  }

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

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

  for (int i = 0; i < n / 2; i++) {
//2026年2月16日修正 修正理由具体的な数字3が入ってしまっていた。

    u = a[i][(2 + i) % (n / 2)];
    //a[i][(2 + i) % (n / 2)]のデータがa[i][n - 1 - ((2 + i) % (n / 2))]
    // のデータ上書きされる前にuに保存

    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))]のデータで上書き

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

  }

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


  //緑の部分の交換終了

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

    u = 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)]
    // のデータに上書きされる前にuに保存

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

  }

  //緑の部分の交換終了

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

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

  int t[2] = { 0,0 };//tの定義と初期化

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

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

      g[i] = g[i] + a[i][j];

    }

  }

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

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

      r[i] = r[i] + a[i][j];

    }

  }

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

    t[0] = t[0] + a[i][i];

    t[1] = t[1] + a[i][n - 1 - i];

  }

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

  cout << endl;

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

    cout << " ";

  }

  cout << " " << t[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 << " " << g[i];

    cout << endl;

  }

  cout << endl;

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

    cout << r[i] << " ";

  }

  cout << " " << t[0] << " ";

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

}
(以上コピペ用コード、
上のコード最初はわかりにくいかもしれませんが、
熟れればこちらの方がわかりやすくなります。)

ただし、せっかく苦労して作った方陣ですから、

  int c[n * n];//コピーするためのa
1
を定義してコピーしておきましょう。

並び替えはc[n * n]を使いましょう。

つまり、魔方陣が収納してあるa[n][n]はいじらずにとっておくことにします。


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

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

ヒントです。

2次元データを1次元データにするときは
  for (int i = 0; i < n; i++) {

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

      c[n * i + j] = a[i][j];

    }

  }

のようにします。

2次元を1次元に直す作業は魔方陣自動生成や数独自動生成を

後に学ぶ関数の再帰的方法使う際には絶対に必要な作業です。

私の魔方陣研究のブレークスルーは2次元は1次元で表すことができる!です。

そして、魔方陣自動生成のプログラムを改良して数独自動生成ソフトを開発したのです。





第6章第9話へ 第6章第11話へ

本講義トップへ