C++で数独の有効性を判定するアルゴリズムを解説
9×9の行列で表される数独(Sudoku)が与えられたとします。この課題の目的は、与えられた数独の配置が有効かどうかを判定することです。
一般的な数独の盤面は次のようになります。

数独のルール
- 各行には1〜9の範囲の数字が入る
- 各列には1〜9の範囲の数字が入る
- 各3×3のブロックには重複のない数字が入る
- 同じ行に同じ数字が現れることはできない
- 同じ列に同じ数字が現れることはできない
入出力の例
入力例:
sudoku[]=
[["3","5",".",".","2",".",".",".","."]
,["7",".",".","1","6","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","5",".","4",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]出力: True
解説: 数独行列内のすべての数字が有効な数独のパターンに従っているため、出力は True になります。なお、「.」は空のマス(未入力のセル)を表し、空のマスは判定の対象外となります。
解決のためのアプローチ
まず、与えられた数独盤面の各列に重複した数字がないかを確認します。次に各行をチェックし、さらに各3×3ブロック内で数字がすべて一意であるかを確認します。ブロックの行と列を順に走査し、重複する数字が1つでも見つかれば false を返し、すべてのチェックを通過すれば true を返します。
具体的な手順は以下の通りです。
- 数独盤面を2次元配列として入力として受け取る
- 行内の要素が一意であるかを判定するブール関数を用意する
- 列内の要素が一意であるかを判定するブール関数を用意する
- 3×3ブロック内の要素が一意であるかを判定するブール関数を用意する
C++での実装例
#include<bits/stdc++.h>
using namespace std;
bool validSudoku(vector<vector<char>>& sudoku) {
int row = 0, col = 0, i = 0, block = 0;
int count[9];
// 各行のチェック
for (row = 0; row < 9; ++row){
memset(count, 0, 9 * sizeof(int));
for (col = 0; col < 9; ++col){
if (sudoku[row][col] != '.')
++count[sudoku[row][col]-'1'];
}
for (i = 0; i < 9; ++i)
if (count[i] > 1)
return false;
}
// 各列のチェック
for (col = 0; col < 9; ++col){
memset(count, 0, 9 * sizeof(int));
for (row = 0; row < 9; ++row){
if (sudoku[row][col] != '.')
++count[sudoku[row][col]-'1'];
}
for (i = 0; i < 9; ++i)
if (count[i] > 1)
return false;
}
// 各3×3ブロックのチェック
int block_row = 0, block_col = 0;
for (block = 0; block < 9; ++block){
block_row = (block / 3) * 3, block_col = (block % 3) * 3;
memset(count, 0, 9 * sizeof(int));
for (row = block_row; row < (block_row + 3); ++row)
for (col = block_col; col < (block_col + 3); ++col)
if (sudoku[row][col] != '.')
++count[sudoku[row][col] - '1'];
for (i = 0; i < 9; ++i)
if (count[i] > 1)
return false;
}
return true;
}
int main(){
vector<vector<char> > sudoku= {
{'5','3','.','.','7','.','.','.','.'},
{'6','.','.','1','9','5','.','.','.'},
{'.','9','8','.','.','.','.','6','.'},
{'8','.','.','.','6','.','.','.','3'},
{'4','.','.','8','.','3','.','.','1'},
{'7','.','.','.','2','.','.','.','6'},
{'.','6','.','.','.','.','2','8','.'},
{'.','.','.','4','1','9','.','.','5'},
{'.','.','.','.','8','.','.','7','9'}
};
bool ans= validSudoku(sudoku);
if(ans){
cout<<"True"<<endl;
} else {
cout<<"false"<<endl;
}
return 0;
}実行結果
True
アルゴリズムのポイント
この実装では、出現回数を記録する配列 count[9] を用意し、memset で毎回初期化することで、行・列・ブロックごとに独立したカウントを実現しています。文字 '1' からのオフセットをインデックスとして使うことで、数字 '1'〜'9' を配列の添字 0〜8 にマッピングしています。
計算量は盤面が固定サイズ(9×9)であるため O(1) ですが、一般化すると O(N²) の時間計算量、O(N) の空間計算量となります。空きマス「.」はスキップされるため、未完成の数独盤面に対しても有効性の判定が可能です。
-
C++で数独を解く!バックトラッキングによる数独ソルバーの実装方法
9×9のマス目に並んだ数字のパズル「数独(Sudoku)」を、プログラムで自動的に解く方法を解説します。数独は9×9の数字グリッドから成り、その全体がさらに3×3のブロック(ボックス)に分割されているのが特徴です。数独を解くための基本ルール使用するのは1から9までの数字のみです。同じ行、同じ列、同じ3×3ブロック内に、同じ数字を重複させて配置することはできません。バックトラッキングによる解法ここでは「バックトラッキング」という手法を用いて数独を解きます。バックトラッキングとは、空いているセルに仮に数字を入れてみて、その配置が正しいかどうかを検証する方法です。もし配置が不正であれば別の数字を試し
-
Pythonで数独の盤面が有効かどうかを判定する方法
問題の概要 9×9の数独(Sudoku)盤面が与えられ、その盤面が有効(valid)であるかどうかを判定します。検証対象はすでに数字が埋められているセルのみであり、以下の3つのルールを満たす必要があります。 行のルール:各行には数字1〜9が重複なく含まれていること 列のルール:各列には数字1〜9が重複なく含まれていること ブロックのルール:盤面を区切った9つの3×3サブボックスそれぞれに、数字1〜9が重複なく含まれていること 注意したいのは、盤面が完成していなくてもよいという点です。空欄は無視し、埋まっている数字だけがルールに違反していないかを確認します。 例として、次の数独盤面を考えてみ