第31講 数独(ナンバープレイス)問題解決ソフトVer.2の制作
(数独(ナンバープレイス)問題作成ソフトに挑戦する人は☆☆)


第6話 
問題全体構造解析のコード解説その1

コード再掲
        void zentaikouzoukaiseki(){
          char i,j,k,l,x,y,k1=0,k2=0;
          for(i=0;i<9;i++){
            for(j=0;j<9;j++){
              if(a[i][j]==0){
                b[i][j]=0;
                for(k=0;k<9;k++){
                  lst[i][j][k]=1;  //リストするかどうかのマルバツに相当するもの 最初すべて1すなわち○にしておく。
                  rlst[i][j][k]=0; //realリストでrを付けてある。上のオンオフにしたがって、実際にリスト(登録)する配列。
                }
              }
            }
          }
          for(i=0;i<9;i++){
            y=i/3;  //ブロックが何行目(ブロック単位で数えた行数)をしめす。具体的には、0,1,2。
            for(j=0;j<9;j++){
              if(a
[i][j]==0){
                for(k=0;k<9;k++)if(k!=i)if(a
[k][j]>0)lst[i][j][a[k][j]-1]=0;
                for(k=0;k<9;k++)if(k!=j)if(a
[i][k]>0)lst[i][j][a[i][k]-1]=0;
                x=j/3;  //ブロックが何列目(ブロック単位で数えた列数)を示す。具体的には0,1,2。
                for(k=0;k<3;k++)for(l=0;l<3;l++)if(3*y+k!=i && 3*x+l!=j)if(a
[3*y+k][3*x+l]>0)lst[i][j][a[3*y+k][3*x+l]-1]=0;
                for(k=0;k<9;k++){
                  if(lst[i][j][k]==1){
                    rlst[i][j][b[i][j]]=k+1; //lstがオン(○)とき、実際にリストする。
                    b[i][j]++; //リスト個数を数える配列。
                  }
                }
              }
            }
          }
          
//以降は、全体構造解析がうまくっているかを確認するためデータグリッドビューに表示させるためのもの。
          for(i=0;i<13;i++){
            if(i%4==0){
              k1++;
              for(j=0;j<13;j++)dataGridView1[j,i+14]->Value=L"*";
            }
            if(i%4>0){
              k2=0;
              for(j=0;j<13;j++){
                if(j%4==0){
                   dataGridView1[j,i+14]->Value=L"*";
                   k2++;
                }
                if(j%4>0){
                  if(a[i-k1][j-k2]==0)dataGridView1[j,i+14]->Value=b[i-k1][j-k2];
                  if(a[i-k1][j-k2]>0)dataGridView1[j,i+14]->Value=L"/";
                }
              }
            }
          }

        }
解説
コード解説に入りますが、この解説を読む前に第3話 問題全体構造解析はどのようにしたら出来るか
をもう一度熟読して下さい。
ある程度慣れてきた読者なら、第3話の考え方とコードを比較すると大体はお分かりかと思います。
しかし、あくまでまったくプログラミングのない方を対象とする講義ですので、
詳しく説明していきます。

最初に大事な注意を説明しておきます。

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

今回のコードを理解するためには、主体となるセル(対象セル)と客体セル(対象以外のセルで対象と比べられるセル)
を区別することが大事です。以下の記述において

が主体となるセルです。客体セルは、この主体セルと比べられます。
[i][j]
は主体セルの座標を示し、[k][j][i][k][3*y+k][3*x+l]は主体セルと比べられる客体セルの座標です。
[k][j]は主体セルと同列にあるセルの座標ですし、[i][k]同行にあるセルの座標です。
最後、[3*y+k][3*x+l]は同ブロックにあるセルです。


まず、
          for(i=0;i<9;i++){
            for(j=0;j<9;j++){
              if(a[i][j]==0){
                b[i][j]=0;
                for(k=0;k<9;k++){
                  lst[i][j][k]=1;  //リストするかどうかのマルバツに相当するもの 最初すべて1すなわち○にしておく。
                  rlst[i][j][k]=0; //realリストでrを付けてある。上のオンオフにしたがって、実際にリスト(登録)する配列。
                }
              }
            }
          }

は準備です。初期化をしています。
lst[i][j][k]=1;

座標(i,j)のリスト 

座標(i,j)のリストをいったんすべて○にしています。
そして、行・列・ブロックの中にそれらの数字が存在していたら×(実際には0)に変更します。
rlst[i][j][k]は、最終的なリスト

座標(3,4)のリスト 
× × × × ×

に基づいて、実際にリストします。lstは、オン・オフ(マル・バツ)のみであるのに対して、rlstはオン(○)のときその数字を実際にリストします。
実際にリストするのでrealという意味でrを付けました。
変数名を考えるのは、余り得意ではありません。
英語が苦手でドイツ語で大学を受けたぐらいですから、私の英語の力はしれたものです。
変数名を英単語にしない本当の理由は、英語が苦手であると言うことでしょうか。
ただ、ドイツ語で大学を受けたことでヘーゲルのエンチクロペディやマルクスの資本論をドイツ語で読めましたので、、
英語が苦手であったことを感謝しています。

次の、
          for(i=0;i<9;i++){
            y=i/3;  //ブロックが何行目(ブロック単位で数えた行数)をしめす。具体的には、0,1,2。
            for(j=0;j<9;j++){
              if(a[i][j]==0){
                for(k=0;k<9;k++)if(k!=i)if(a[k][j]>0)lst[i][j][a[k][j]-1]=0;
                for(k=0;k<9;k++)if(k!=j)if(a[i][k]>0)lst[i][j][a[i][k]-1]=0;
                x=j/3;  //ブロックが何列目(ブロック単位で数えた列数)を示す。具体的には0,1,2。
                for(k=0;k<3;k++)for(l=0;l<3;l++)if(3*y+k!=i && 3*x+l!=j)if(a[3*y+k][3*x+l]>0)lst[i][j][a[3*y+k][3*x+l]-1]=0;
                for(k=0;k<9;k++){
                  if(lst[i][j][k]==1){
                    rlst[i][j][b[i][j]]=k+1; //lstがオン(○)とき、実際にリストする。
                    b[i][j]++; //リスト個数を数える配列。
                  }
                }
              }
            }
          }

が難解ですね。
if(a[i][j]==0)はセルが空白ときだけ、対象にするために必要なものです。
問題によって入力されているセルは、解析をしても意味がありませんし、時間の無駄です。
そもそも、問題によって入力されているセルは、いじってはいけませんね。
いじらないようにするためにif(a[i][j]==0)があちらこちらに登場します。


for(k=0;k<9;k++)if(k!=i)if(a[k][j]>0)lst[i][j][a[k][j]-1]=0;
は行に数字がないか調べています。

6 3

数字があった場合、その数字はリストから外さなければなりません。この例の場合6と3です。
それで、lst[i][j][a[k][j]-1]=0なのですが、配列の添え字は0から始まることに注意して下さい。
a[k][j]-1となっている理由です。
lst[i][j][a[k][j]-1]=0の意味は、同じ行にa[k][j]という数字がある場合は、座標(i,j)のリストからa[k][j]を外しなさいという意味です。
for(k=0;k<9;k++)if(k!=i)if(a[k][j]>0)lst[i][j][a[k][j]-1]=0;
for(k=0;k<9;k++){
  if(k!=i){
    if(a[k][j]>0){
      lst[i][j][a[k][j]-1]=0;
    }
  }
}

を1行に縮めたものです。for文やif文の内容が1行しかないとき、このように縮められます。
本講義では、わかりやすさを優先して余り使ってきませんでしたが、
プログラム全体を見通すときにはこの表現の方がよいかもしれませんね。

8
3


for(k=0;k<9;k++)if(k!=j)if(a[i][k]>0)lst[i][j][a[i][k]-1]=0;
は列について同じことをしています。

この場合は8と3がリストから外されます。


























さて、一番わかりにくいのがfor(k=0;k<3;k++)for(l=0;l<3;l++)if(3*y+k!=i && 3*x+l!=j)if(a[3*y+k][3*x+l]>0)lst[i][j][a[3*y+k][3*x+l]-1]=0;です。
これは、
for(k=0;k<3;k++){
  for(l=0;l<3;l++){
    if(3*y+k!=i && 3*x+l!=j)
      if(a[3*y+k][3*x+l]>0){
        lst[i][j][a[3*y+k][3*x+l]-1]=0;
      }
    }
  }
}

を1行に縮めてあります。
9行に広げても、さっぱり意味がわからないというのが正直なところではないでしょうか。
解説は次話で。





第5話へ 第7話へ

戻る

VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)