第6講 残された課題の解消とマルチスレッド化
第7話 C++シングル全文
HTMLファイルをコピペしてエラーするケースは

赤の四角でのところに全角スペースが入っている場合ですので、
赤線の波線が出ている場合はDeleteキーかBackspaceで詰めて

とすればビルドエラーないしはデッバクエラーは回避できます。
尚、ホームページビルダー22だと全角スペースを使ってもビルドエラーはしません。
旧バージョンではエラーしましたからジャストシステム何か工夫を加えてエラーしないようになっています。

#pragma warning(disable: 4996)
#include<iostream>
#include<conio.h> //while (!_kbhit())を使うために必要
using namespace std;
void f(char s); //sは次元番号=部屋番号
const char n = 9; //16次数独や25次数独も考えてnと一般化した。
char cn = 0; //順列を数える変数
char cn1 = 0; //順列を数える変数
char gensudoku[n][n]; //解答用2次元配列(魔方陣の研究から始まったので本体をmとしてきた。
char sudoku[n][n]; //解答用2次元配列(魔方陣の研究から始まったので本体をmとしてきた。
char mondai[n][n]; //sudoku[n][n]は解答用なので、問題用の2次元配列を用意した。
char sng = 1;//1ならば真
const char hnt = 24;
//ヒント数を変えてデバッグしてProject3を24と名前を変更して25から36までの
//実行ファイルを蓄えてから最後にまとめてsudokusのフォルダに移動させる
const char tm = 81;
void syokika();
void hy();//結果をコンソール画面に表示する関数
void kyokusyokaiseki(char y, char x);//単セル解析(候補数字探索)
void nextcell(char g);//次に入力するセルを探索する関数
void sudokukaiho(char g);//数独を解くエンジン 部分構造解析を進めながら問題を解いていく
char lst[9][9][9];//セルリスト構造解析によって可能な数字を収容する3次元配列
char mx[9][9];//セルの数字候補の個数
char Y[81], X[81];//次に入力するセルのy座標とx座標
unsigned u = (unsigned)time(NULL);
//マルチスレッド化した際に、
//ルートスレッドと発生させたスレッドが共有できるようにグローバル変数に変更
char main() {
  clock_t hj, ow;
  srand(u);
  while (1) {
    syokika();
    f(0);
    for (char i = 0; i < n; i++) {
      for (char j = 0; j < n; j++) {
        if (mondai[i][j] == 0)kyokusyokaiseki(i, j);
        //セルリスト構造解析 = 単セル解析を積み重ねれば全体構造解析になる
      }
    }
    sudokukaiho(hnt);//数独を解くエンジン 部分構造解析を進めながら問題を解いていく
    if (cn1 == 1)break;
  }
//以降CSVファイルの作成
  FILE* fp;
/*ファイル(save.csv)に書き込む*/
  if ((fp = fopen("a.csv", "w")) != NULL) {
    for (unsigned char i = 0; i < n; i++) {
      for (unsigned char j = 0; j < n; j++) {
        fprintf(fp, "%d,\n", gensudoku[i][j]);//数独
      }
    }
    for (unsigned char i = 0; i < n; i++) {
      for (unsigned char j = 0; j < n; j++) {
        fprintf(fp, "%d,\n", sudoku[i][j]);//数独解答
      }
    }
  }
/*忘れずに閉じる*/
  fclose(fp);
  cout << "プロジェクト成功" << endl;
  //while (!_kbhit()); //待機させるための命令
  return(0);
}
void syokika() {
  cn = 0;
  cn1 = 0;
  for (char i = 0; i < n; i++) {
    for (char j = 0; j < n; j++) {
      gensudoku[i][j] = 0;
      sudoku[i][j] = 0;
      mondai[i][j] = 0;
    }
  }
}
void hy() {
  for (char i = 0; i < 26; i++)cout << " -"; //最初の横線
  cout << endl;
  for (char i = 0; i < n; i++) {
    cout << "|";//最初の縦線
    for (char j = 0; j < n; j++) {
      if (gensudoku[i][j] > 0) {
        cout << " " << +gensudoku[i][j] << " "; //2次元配列を2次元に並べる
      }
      else {
        cout << " " << " " << " "; //2次元配列を2次元に並べる
      }
      if (j % 3 == 2)cout << "|";//2本目3本目の縦線
    }
    if (i % 3 == 2) {
      cout << endl;
      for (char j = 0; j < 26; j++)cout << " -"; //2本目3本目の横線
    }
    cout << endl;
  }
}
void kyokusyokaiseki(char y, char x) {//局所解析 = 単セルリスト構造解析
  for (char i = 0; i < n; i++)lst[y][x][i] = i + 1;//初期化{1,2,3,4,5,6,7,8,9}とする
  mx[y][x] = 0;//{1,2,3,4,5,6,7,8,9}を{2,4}等にするために0に初期化 再カウントのため
  for (char i = 0; i < n; i++) {//mondai[y][x]と同じ行にあるセルからの影響を調べる
    if (i != x) {//自分自身は対象にしない
      if (mondai[y][i] > 0) {
        for (char j = 0; j < n; j++) {
          if (lst[y][x][j] == mondai[y][i])lst[y][x][j] = 0;
          //mondai[y][i]と一致する数字を0にすることによって候補から外す
        }
      }
    }
  }
  for (char i = 0; i < n; i++) {//mondai[y][x]と同じ列にあるセルへの影響を調べる
    if (i != y) {//自分自身は対象にしない
      if (mondai[i][x] > 0) {//空欄のみをセル解析の対象にする
        for (char j = 0; j < n; j++) {
          if (lst[y][x][j] == mondai[i][x])lst[y][x][j] = 0;
          //mondai[i][x]と一致する数字を0にすることによって候補から外す
        }
      }
    }
  }
  for (char i = 0; i < n; i++) {//mondai[y][x]と同じブロックにあるセルへの影響を調べる
    if (3 * (y / 3) + (i / 3) != y && 3 * (x / 3) + (i % 3) != x) {
      if (mondai[3 * (y / 3) + (i / 3)][3 * (x / 3) + (i % 3)] > 0) {
        for (char j = 0; j < n; j++) {
          if (lst[y][x][j] == mondai[3 * (y / 3) + (i / 3)][3 * (x / 3) + (i % 3)])lst[y][x][j] = 0;
          //mondai[3 * (y / 3) + (i / 3)][3 * (x / 3) + (i % 3)]と一致する数字を
          //0にすることによって候補から外す
        }
      }
    }
  }
  for (char i = 0; i < n; i++) {
    if (lst[y][x][i] > 0) {
      lst[y][x][mx[y][x]] = lst[y][x][i];//例えば、{0, 2, 0, 4, 0, 0, 0, 0, 0}を{2, 4}と詰めて0を含めない
      mx[y][x]++;
    }
  }
}
void nextcell(char g) {//次に入力するセルの座標を探索する関数
  char mn = 10;
  for (char i = 0; i < n; i++) {
    for (char j = 0; j < n; j++) {
      if (mondai[i][j] == 0) {//空欄のみをランキング対象にする
        if (mx[i][j] < mn) {//<=でないことによって左及び上が優先される
          mn = mx[i][j];
          Y[g] = i;
          X[g] = j;
        }
      }
    }
  }
}
void sudokukaiho(char g) {//数独を解くエンジン
  nextcell(g);//座標(Y[g], X[g])の取得
  for (char i = 0; i < mx[Y[g]][X[g]]; i++) {
    mondai[Y[g]][X[g]] = lst[Y[g]][X[g]][i];
    //候補の数字を代入 例えば、mondai[0][1]なら{2, 4}の2,4の順に代入する
    for (char j = 0; j < n; j++)if (j != X[g])if (mondai[Y[g]][j] == 0)kyokusyokaiseki(Y[g], j);
    //x[Y[g]][X[g]]と同じ行にあるセルの単セル解析(候補数字探索)
    for (char j = 0; j < n; j++)if (j != Y[g])if (mondai[j][X[g]] == 0)kyokusyokaiseki(j, X[g]);
    //x[Y[g]][X[g]]と同じ列にあるセルの単セル解析(候補数字探索)
    char s = 3 * (Y[g] / 3);
    char t = 3 * (X[g] / 3);
    for (char j = 0; j < n; j++) {
      if (s + (j / 3) != Y[g] && t + (j % 3) != X[g]) {//行解析と列解析と重複させないため
        if (mondai[s + (j / 3)][t + (j % 3)] == 0) {
          kyokusyokaiseki(s + (j / 3), t + (j % 3));
          //x[Y[g]][X[g]]と同じブロックにあるセルの単セル解析(候補数字探索)
        }
      }
    }
    if (g + 1 < tm)sudokukaiho(g + 1);//内側部屋へ
    if (cn1 == 2) {
      return;//複数解
    }
    if (g == tm - 1) {
      //hy();//解答表示
      cn1++;
    }
    if (i == mx[Y[g]][X[g]] - 1) {
      //i < mx[Y[g]][X[g]] - 1)のときはiが1つ進んで上で単セル解析(候補数字探索)を行うので
      //キャンセルは不要だが、最後だけはキャンセルをしなければならないので単セル解析(候補数字探索)
      //を行う必要がある
      //cout << "*-*-*-*-復元-*-*-*-*-*" << endl;
      mondai[Y[g]][X[g]] = 0;
      //cout << g << endl;
      for (char j = 0; j < n; j++)if (j != X[g])if (mondai[Y[g]][j] == 0)kyokusyokaiseki(Y[g], j);
      for (char j = 0; j < n; j++)if (j != Y[g])if (mondai[j][X[g]] == 0)kyokusyokaiseki(j, X[g]);
      for (char j = 0; j < n; j++) {
        if (s + (j / 3) != Y[g] && t + (j % 3) != X[g]) {
          if (mondai[s + (j / 3)][t + (j % 3)] == 0) {
            kyokusyokaiseki(s + (j / 3), t + (j % 3));
          }
        }
      }
    }
  }
}
void f(char s) {//ヒント数0の数独を解く関数
  char y = s / 9; //縦座標
  char x = s % 9; //横座標
  char e[9];
  e[0] = rand() % 9 + 1;
  char i = 1;
  while (i < 9) {
    e[i] = rand() % 9 + 1;
    while (1) {
      char h = 1;
      e[i] = rand() % 9 + 1;
      for (char j = 0; j < i; j++) {
        if (e[i] == e[j]) {
          h = 0;
          break;
        }
      }
      if (h == 1)break;
    }
    i++;
  }
  char ii = rand() % 9; //始まりをランダムにする
  for (char i = 0; i < n; i++) {
    sudoku[y][x] = e[(i + ii) % n]; //2次元配列に1から9までの整数を入力
      if (x > 0) {
        for (char j = 0; j < x; j++) {
          if (sudoku[y][x] == sudoku[y][j])goto tobi; //行の重複を防ぐ
        }
      }
      if (y > 0) {
        for (char j = 0; j < y; j++) {
          if (sudoku[j][x] == sudoku[y][x])goto tobi; //行の重複を防ぐ
        }
      }
      if (y % 3 == 1) {
        for (char j = 0; j < 3; j++) {
          if ((x / 3) * 3 + j != x) {
            if (sudoku[y][x] == sudoku[y - 1][(x / 3) * 3 + j])goto tobi; //ブロックの重複を防ぐ
          }
        }
      }
      if (y % 3 == 2) {
        for (char j = 0; j < 3; j++) {
          if ((x / 3) * 3 + j != x) {
            if (sudoku[y][x] == sudoku[y - 2][(x / 3) * 3 + j])goto tobi; //ブロックの重複を防ぐ
            if (sudoku[y][x] == sudoku[y - 1][(x / 3) * 3 + j])goto tobi; //ブロックの重複を防ぐ
          }
        }
      }
      if (s + 1 < n * n)f(s + 1); //1つ奥の部屋に入室
      if (cn == 1)return; //数独が1個できた時点で止める
      if (sng == 0)return; //異常があった時点でプロジェクトを止める
      if (s == n * n - 1) { //一番奥に部屋に到達
      char hb; //部屋番号
      char tbs[13] = { 11,13,17,19,23,29,31,37,41,43,47,53,59 };
       //飛びの選択肢 素数であれば81と互いに素は保証されている
      char st = rand() % 81; //始めの位置
      char tb = tbs[rand() % 13]; //飛び
      char h = 0; //可否の否
      char* gohr = (char*)calloc(hnt, sizeof(char));
      for (char t = 0; t < hnt; t++) {
        gohr[t] = (st + t * tb) % 81;
      }
      for (char j = 0; j < n; j++) {
        for (char k = 0; k < n; k++) {
          hb = n * j + k; //部屋番号の再初期化 = 空欄の数字候補の個数の小さい順に入れる
          char h = 0; //可否の否
          for (char t = 0; t < hnt; t++) { 
            if (gohr[t] == hb) {
              h = 1;//可否の可
              break;
            }
          }
          if (h == 1) {
            mondai[j][k] = sudoku[j][k]; //問題用の配列に代入
            gensudoku[j][k] = sudoku[j][k];//マルチスレッド化するときに必要
          }
        }
      }
      //hy();//問題表示
      free(gohr); //メモリ解放
      cn++;
      if (cn == 1)return; //数独が1個できた時点でとめる
    }
  tobi:;
  }
}

第6話へ
 第8話へ

トップへ