[C/C++解答]1数独回答判別器の作成


ある程度C++を学習した後,符号化利用率を向上させるために関連問題を調べた.
そして次のリンクで数独問題を発見し、解題すれば面白いと思ってエンコードしました.

codemate.kr



実は私たちは上のサイトにも答えをアップロードしていますが、開発中にも書けばもっといいので、位置付けをしてみましょう.

コードコメントのコメント


0、問題を分析し、データ構造を考える


まず数独問題を単純化しよう


数独が正しいか間違っているかを判断する方法は以下の通りです.
  • 同じ行は
  • を重ねることができる.
  • は、同じ列において、
  • を重ねることができる.
  • 等ブロック(?)
  • 簡単に考えると,第1条件と第2条件の判別方式は非常に似ている可能性がある.でも3つ目の条件はちょっと違う
    水平値と垂直値の違いとブロック値の違いが異なる場合がありますので、この2つの結果をそれぞれ入力することをお勧めします.
    また、ディスクライブラリは9×9マトリクスであるため、int型冗長アレイを直感的に使用することが望ましい.
    そこで、最終的に次のデータ構造を作成しました.
    class Table{
        private :
        int array[9][9];
    
        bool first_result = true;           // 가로, 세로 확인한 결과
        bool second_result = true;          // 블록값 확인한 결과
    
    }
    
    Tableという名前のクラスが作成されました.もちろん、関数と破壊子を構築する必要があります.
    また、水平、垂直、ブロック値を識別する関数も必要です.これをクラス外部関数として作成する場合は、関数を呼び出すたびにTableクラスの情報を入力する必要があります.
    これは面倒かもしれませんが、クラス内でメソッドを作成しましょう.
    class Table{
        private :
        int array[9][9];
    
        bool first_result = true;           // 가로, 세로 확인한 결과
        bool second_result = true;          // 블록값 확인한 결과
    
        public :
        Table(int (*input_array)[9]){
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++){
                    array[i][j] = input_array[i][j];
                }
            }
        }
        Table(){}
        ~Table(){}
    
        bool check_row(int current_row);                        // 가로 확인하는 함수
        bool check_column(int current_column);                  // 세로 확인하는 함수
        bool check_block(int current_row, int current_column);  // 블록 확인하는 함수
        
        void total_check();   // 위 세 함수 같이 생각할 함수
    
    
        void print_array(){   // 내부 array print
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++){
                    std::cout << array[i][j] << " ";
                }
                std::cout << std::endl;
            }
        }
    };
    

    1.同じ行を重ねることができるか

    bool Table::check_row(int current_row){    // 가로값 확인하는 함수
    
        for (int i = 0; i < 9; i++){
            int first_value = array[current_row][i];
    
            for (int j = i + 1; j < 9; j++){
                if (first_value == array[current_row][j]) {return false;}
            }
        }
    
        return true;
    }
    
    行に重複があるかどうかを判別する関数を作成しました.関数のパラメータは、チェックする行(int current row)です.
    for文を回転させると、配列の値がfirst valueと同じであることを確認します.
    同じ値がある場合は、return falseで直接終了します.
    first value=array[current row][i]のため、first valueは行の1番目、2番目の~~値に変わり続けます.
    次のfor文はintj=i+1と呼ばれるため、first valueの後の値のみがチェックされます.
    中間にfirst valueと同じ値がない場合はtrueを返します.

    2.同一列で重複可能か

    bool Table::check_column(int current_column){   // 세로값 확인하는 함수
    
        for (int i = 0; i < 9; i++){
            int first_value = array[i][current_column];
    
            for (int j = i + 1; j < 9; j++){
                if (first_value == array[j][current_column]) {return false;}
            }
        }
    
        return true;
    }
    前述したように、垂直値をチェックする関数は、前の関数とほぼ同じです.
    first value設定が前の関数と同じで、中間値が同じ場合falseが返されます.

    3.同じブロックに重ねられるか


    ブロック値をチェックするときに注意したいのは、チェックすべき値が前の関数のように横と下に直接ないことです.しかし、それ以外に、何かを同じにすることができるようです.
    bool Table::check_block(int current_row, int current_column){   // 블록값 확인하는 함수
    
        // 이 함수는 각 블록값의 첫번째 원소의 row, column을 인수로 받음.
    
        for (int i = current_row; i < current_row + 3; i++){
            for (int j = current_column; j < current_column + 3; j++){
                
                // i, j 가 증가하면서 첫번째, 두번째 원소값 등 계속 올라감.
                int first_value = array[i][j];  
    
                int count = 0;
                // first_value와 같은 값을 같는 원소 수를 셈.
    
                for (int k = i; k < current_row + 3; k++){
                    for (int l = j; l < current_column  + 3; l++){
                          // 만약 first_value와 같은 값이 있다면 count++
                        if (first_value == array[k][l]) {count++;}  
                    }
                }
    
                /* 위의 k, l은 i, j 값과 같은 수부터 시작이므로 블록 내에 같은 값이 있다면 
                count 는 2 이상이 됨. 즉 first_value 와 같은 원소는 자기 자신밖에 없을 
                것이므로 count > 2 면 즉시 return false */
                if (count != 1) {return false;}
            }
        }
    
        // 아무 이상 없으면 return true
        return true;
    }
    
    check block関数は、現在の形状とカラム(int current row,int current column)の2つの変数を受け入れます.チェックする値は横と下にないからです.
    この2つの値を使用すると、ブロック内の最初の配列に位置値を入力できます.
    つまり、次の3つのブロックがあります.
    1  2  3			4  2  1			7  8  9
    4  5  6			3  5  7			1  2  3
    7  8  9			6  8  9			4  5  6
    1番目のブロックは、(1)その位置の値、2回の測定(4)、および3回目の測定(7)を変数として表す.
    最後に,3,4番目のfor文はk=i,l=jから始まる.
    for (int k = i; k < current_row + 3; k++){
       for (int l = j; l < current_column  + 3; l++){
           // 만약 first_value와 같은 값이 있다면 count++
          if (first_value == array[k][l]) {count++;} 
       }
    }
    
    上でfirst value=array[i][j]が設定されているため、for文がループするとfirst valueと同じ値が表示されます.だからcount!=1でfalseを返し、異常がなければtrueを返します.

    4.総合的に考える


    今が最後の一歩です.上に作成した3つの関数は、1行、1列、または1つのブロックでのみ識別される関数なので、すべての部分の確認部分を作成する必要があります.
    void Table::total_check(){
        std::cout << std::endl;
    
        for (int i = 0; i < 9; i++){        // 일단 가로 확인
        	// first_result에 true or false 넣음
            first_result = check_row(i);    
            if (first_result == false) {std::cout << "False" << std::endl; return;}
        }
        for (int i = 0; i < 9; i++){        // 세로 확인
        	// first_result에 true or false 넣음
            first_result = check_column(i); 
            if (first_result == false) {std::cout << "False" << std::endl; return;}
        }
    
    	/* 블록값 확인 : 각 블록값 첫번째 원소의 row(i), column(j) 을
        check_block 함수에 인자로 넣음*/
        for (int i = 0; i != 9; i += 3){    
            for (int j = 0; j != 9; j += 3){
                second_result = check_block(i,j);       
                // (i,j) : (0,0), (0,3), (0,6), (3,0) ~~ 이런식
                if (second_result == false) {std::cout << "False" << std::endl; return;}
            }
        }
    
        std::cout << "True" << std::endl;
    }
    
    各for文について、結果値がfalseの場合、すぐに停止します.
    また、最後のfor文を表示すると、i+=3、j+=3の各ブロックの最初の位置値が得られます.
    (i,j) : (0,0), (0,3), (0,6), (3,0) ~~> (6,6) 
    今までやってきたことを総合してみると、以下のようになります.
    #include <iostream>
    
    class Table{
        private :
        int array[9][9];
    
        bool first_result = true;           // 가로, 세로 확인한 결과
        bool second_result = true;          // 블록값 확인한 결과
    
        public :
        Table(int (*input_array)[9]){
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++){
                    array[i][j] = input_array[i][j];
                }
            }
        }
        Table(){}
        ~Table(){}
    
        bool check_row(int current_row);                        // 가로 확인하는 함수
        bool check_column(int current_column);                  // 세로 확인하는 함수
        bool check_block(int current_row, int current_column);  // 블록 확인하는 함수
        
        void total_check();                                     // 위 세 함수 같이 생각할 함수
    
        void print_array(){
            for (int i = 0; i < 9; i++){
                for (int j = 0; j < 9; j++){
                    std::cout << array[i][j] << " ";
                }
                std::cout << std::endl;
            }
        }
    };
    
    int main(){
        std::cout << "\ncode_start\n\n" << std::endl;
        
        int array[9][9];
        for (int i = 0; i < 9; i++){
            for (int j = 0; j < 9; j++){
                int temp;
                std::cin >> temp;
                array[i][j] = temp;
            }
            std::cout << std::endl;
        }
        
        Table test(array);
        test.print_array();
    
        test.total_check();
        
        std::cout << "\n\ncode_end\n" << std::endl;
        return 0;
    }
    
    
    bool Table::check_row(int current_row){		// 가로값 확인하는 함수
    
        for (int i = 0; i < 9; i++){
            int first_value = array[current_row][i];
    
            for (int j = i + 1; j < 9; j++){
                if (first_value == array[current_row][j]) {return false;}
            }
        }
    
        return true;
    }
    bool Table::check_column(int current_column){	// 세로값 확인하는 함수
    
        for (int i = 0; i < 9; i++){
            int first_value = array[i][current_column];
    
            for (int j = i + 1; j < 9; j++){
                if (first_value == array[j][current_column]) {return false;}
            }
        }
    
        return true;
    }
    bool Table::check_block(int current_row, int current_column){   
    // 블록값 확인하는 함수
    
        // 이 함수는 각 블록값의 첫번째 원소의 row, column을 인수로 받음.
    
        for (int i = current_row; i < current_row + 3; i++){
            for (int j = current_column; j < current_column + 3; j++){
                
                int first_value = array[i][j];      
                // i, j 가 증가하면서 첫번째, 두번째 원소값 등 계속 올라감.
    
                int count = 0;
                // first_value와 같은 값을 같는 원소 수를 셈.
    
                for (int k = i; k < current_row + 3; k++){
                    for (int l = j; l < current_column  + 3; l++){
    	                // 만약 first_value와 같은 값이 있다면 count++
                        if (first_value == array[k][l]) {count++;}  
                    }
                }
    
                /* 위의 k, l은 i, j 값과 같은 수부터 시작이므로 블록 내에 같은 값이 있다면 
                count 는 2 이상이 됨. 즉 first_value 와 같은 원소는 자기 자신밖에 없을 
                것이므로 count > 2 면 즉시 return false*/
    
                if (count != 1) {return false;}
            }
        }
    
        // 아무 이상 없으면 return true
        return true;
    }
    
    void Table::total_check(){
        std::cout << std::endl;
    
        for (int i = 0; i < 9; i++){        // 일단 가로 확인
            first_result = check_row(i);    // first_result에 true or false 넣음
            if (first_result == false) {std::cout << "False" << std::endl; return;}
        }
        for (int i = 0; i < 9; i++){        // 세로 확인
            first_result = check_column(i); // first_result에 true or false 넣음
            if (first_result == false) {std::cout << "False" << std::endl; return;}
        }
    
        for (int i = 0; i != 9; i += 3){    // 블록값 확인 : 각 블록값 첫번째 원소의 row(i), column(j) 을 check_block 함수에 인자로 넣음
            for (int j = 0; j != 9; j += 3){
                second_result = check_block(i,j);       
                // (i,j) : (0,0), (0,3), (0,6), (3,0) ~~ 이런식
                if (second_result == false) {std::cout << "False" << std::endl; return;}
            }
        }
    
        std::cout << "True" << std::endl;
    }
    

    5.総評


    初めて数独を見た時は適当な難易度だと思ったけど思ったより簡単
    次は、より適切な問題を提起する必要があります.ありがとうございます.