第19講 数独(ナンプレ)解答自動生成アプリ

第5話 ブロックの条件を付け加えて数独(ナンプレ)解答自動生成アプリの一応の完成
実行画面
2 5 3 1 4 7 9 6 8

6 9 8 5 1 2 4 3 7
3 1 4 8 7 9 6 2 5
4 2 5 7 6 3 8 9 1
7 3 6 4 2 5 1 8 9
9 4 1 6 3 8 5 7 2
5 6 2 9 8 1 7 4 3
1 8 7 3 9 6 2 5 4
8 7 3 2 5 4 9 1 6
2 5 9 1 4 7 3 6 8

6 9 8 5 1 2 4 3 7
3 1 4 8 7 9 6 2 5
4 2 5 7 6 3 8 9 1
7 3 6 4 2 5 1 8 9
9 4 1 6 3 8 5 7 2
5 6 2 9 8 1 7 4 3
2 8 7 1 9 4 3 5 6
8 7 9 3 5 6 2 1 4
1 5 3 2 4 7 9 6 8

生成された数独(ナンプレ)は10個です。
数独(ナンプレ)生成にかかった時間は0.000000秒です。

実現するソフト例
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void f(int g); //数独(ナンプレ)を作り出す社員
void hy(); //出来た数独(ナンプレ)をコンソールに表示させる社員
void zy(); //座標を作成する社員
int cn,n;
int m[10][10]; //少し大きめに配列要素数を取っておく
int y[100],x[100];
int main(){
  clock_t hj,ow;
  srand(1);
  n=9;
  hj=clock();
  zy();
  f(0);
  ow=clock();
  printf("生成された数独(ナンプレ)は%d個です。\n",cn);
  printf("数独(ナンプレ)生成にかかった時間は%f秒です。\n",(double)(ow - hj) / CLOCKS_PER_SEC);
}
void zy(){ //座標を作成する社員
  int i;
  for(i=0;i<n*n;i++){
    y[i]=i/n;
    x[i]=i%n;
  }
}
void f(int g){ //数独(ナンプレ)を作り出す社員
  int i,j,ih,k;
  ih=rand()%n;
  for(i=0;i<n;i++){
    for(j=0;j<x[g];j++){
      if(((ih+i)%n)+1==m[y[g]][j])goto tobi;
    }
    for(j=0;j<y[g];j++){
      if(((ih+i)%n)+1==m[j][x[g]])goto tobi;
    }
    
for(j=3*(y[g]/3);j<=y[g];j++){
      for(k=3*(x[g]/3);k<x[g];k++){
        if(((ih+i)%n)+1==m[j][k])goto tobi;
      }
    }

    m[y[g]][x[g]]=((ih+i)%n)+1;
    if(g+1<n*n){
      f(g+1);
      if(cn==10)return;
    }
    else{
      hy();
      cn++;
      if(cn==10)return;
    }
    tobi:;
  }
}
void hy(){ //出来た数独(ナンプレ)をコンソールに表示させる社員
  int i,j;
  for(i=0;i<n;i++){
    for(j=0;j<n;j++){
      printf("%d ",m[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
数独(ナンプレ)解答自動生成アプリ完成一歩前バージョン

      for(k=3*(x[g]/3);k<=x[g];k++){
としてしまうと、数独は1つも出来なくなります。
理由は簡単です。
036
6がブロック内と他の数字と重複していないか調べているわけですが、
=を入れてしまうと、6とも比較してしまうことになり、
        if(((ih+i)%n)+1==m[j][k])goto tobi;
が実行されてしまい、
    m[y[g]][x[g]]=((ih+i)%n)+1;
    if(g+1<n*n){
      f(g+1);
      if(cn==10)return;
    }
    else{
      hy();
      cn++;
      if(cn==10)return;
    }
処理が飛ばされてしまいますので、
数独は1つも出来ないで処理を終えてしまいます。
ですから、
036
6の1つ手前の3までの比較ですから、
      for(k=3*(x[g]/3);k<x[g];k++){
でなければならないのです。
と、解説を書いている段階で、
    
for(j=3*(y[g]/3);j<=y[g];j++){
      for(k=3*(x[g]/3);k<x[g];k++){
        if(((ih+i)%n)+1==m[j][k])goto tobi;
      }
    }

=をとってしまって、
    for(j=3*(y[g]/3);j<y[g];j++){
      for(k=3*(x[g]/3);k<x[g];k++){
        if(((ih+i)%n)+1==m[j][k])goto tobi;
      }
    }

で十分であることに気がつきました。
036
だって、その前の行検査と列検査で、
2,3と9については、すでに重複検査が行われていたからです。
とすると、さらに、
    for(j=3*(y[g]/3);j<y[g];j++){
      for(k=3*(x[g]/3);k<x[g];k++){
        
if(j<>x[g])if(((ih+i)%n)+1==m[j][k])goto tobi;
      }
    }

としてもよいですね。
つまり、
036
9と比較させないのです。
といっても、
        if(j<>x[g])if(((ih+i)%n)+1==m[j][k])goto tobi;
としたからといって、処理が速くなるとは期待は出来ないですよね。
結局は、9のときにif文処理をさせているのですから、
9については、検査が重複することは気にしないようにした方が良さそうです。

さて、
void hy(){ //出来た数独(ナンプレ)をコンソールに表示させる社員
  int i,j;
  for(i=0;i<n;i++){
    for(j=0;j<n;j++){
      printf("%d ",m[i][j]);
    }
    printf("\n");
  }
  printf("\n");
}
を改良して、境界線を入れて
* 1 6 9 * 2 8 3 * 7 5 4 *
* 4 7 3 * 9 5 6 * 2 1 8 *
* * * * * * * * * * * * *

* * * * * * * * * * * * *
* 6 9 8 * 5 1 2 * 4 3 7 *
* 3 1 4 * 8 7 9 * 6 2 5 *
* 5 2 7 * 3 6 4 * 8 9 1 *
* * * * * * * * * * * * *
* 7 3 6 * 4 2 5 * 1 8 9 *
* 9 4 1 * 6 3 8 * 5 7 2 *
* 2 8 5 * 7 9 1 * 3 4 6 *
* * * * * * * * * * * * *
* 8 5 2 * 1 4 7 * 9 6 3 *
* 1 7 3 * 9 8 6 * 2 5 4 *
* 4 6 9 * 2 5 3 * 7 1 8 *
* * * * * * * * * * * * *

生成された数独(ナンプレ)は10個です。
数独(ナンプレ)生成にかかった時間は0.000000秒です。

を実現しましょう。
結構難しいですよ。



第4話へ 第6話へ

a


初心者のための excel 2016 マクロ VBA 入門講義 基礎から応用まで
vc++ c言語 c++ 入門 初心者 基礎から応用まで
eclipse c++ 入門
魔方陣 数独(ナンプレ)で学ぶ VBA 入門

数独(ナンプレ)のシンプルな解き方・簡単な解法の研究
VB講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座

初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 C++ 入門 基礎から応用まで第1部
eclipse java 入門
java 入門 サイト 基礎から応用まで
本サイトトップへ