Sudoku-Configurationが有効かどうかを確認する簡単なアルゴリズムを知っている人はいますか?私が思いついた最も簡単なアルゴリズムは、(サイズnのボードの)疑似コードです
for each row
for each number k in 1..n
if k is not in the row (using another for-loop)
return not-a-solution
..do the same for each column
しかし、私は(よりエレガントな意味で)より良い解決策があるに違いないと確信しています。効率は非常に重要ではありません。
あなたは数独のすべての制約をチェックする必要があります:
これは6つのチェックです。総当たりのアプローチを使用しています。
ボードのサイズ(3x3または9x9)がわかっている場合は、ある種の数学的最適化を使用できます。
編集:合計の制約の説明:最初に合計を確認する(合計が45でない場合は停止する)ことは、重複を確認するよりもはるかに高速(かつ簡単)です。間違ったソリューションを破棄する簡単な方法を提供します。
Peter Norvigが数独パズルを(pythonで)解決することについての素晴らしい記事を持っています、
多分それはあなたがやりたいことには多すぎますが、とにかくそれは素晴らしい読書です
ちょっと考えてみます。各3x3の正方形の数字もチェックする必要はありませんか?
正しい数独がなくても行と列の条件を満たすことができるかどうかを考えています
各行、列、ボックスをチェックして、重複しないようにそれぞれ1〜9の数字が含まれるようにします。ここでのほとんどの回答はすでにこれについて議論しています。
しかし、それを効率的に行う方法は?回答:次のようなループを使用してください
result=0;
for each entry:
result |= 1<<(value-1)
return (result==511);
各数値は結果の1ビットを設定します。 9つの数値すべてが一意である場合、最下位の9ビットが設定されます。したがって、「重複のチェック」テストは、9ビットすべてが設定されていることのチェックにすぎません。これは、テスト結果== 511と同じです。これらのチェックのうち27行を行う必要があります。行、列、ボックスごとに1つずつです。
すべての行、列、正方形のブール値の配列を作成します。配列のインデックスは、その行、列、または正方形に配置されたvalueを表します。つまり、2番目の行の最初の列に5を追加する場合、rows [2] [5]をtrueに設定し、さらにcolumn [1] [5]とsquares [4] [5]を設定して、行、列、正方形に5
値。
元のボードがどのように表現されているかに関係なく、これは、完全性と正確性を確認するためのシンプルで非常に高速な方法です。ボードに表示されている順に番号を取得し、このデータ構造の構築を開始します。ボードに数値を配置すると、O(1)演算になり、特定の行、列、または四角形で値が重複しているかどうかを判断します(また、各値が正当な数値であることを確認します。空白または高すぎる数値を示した場合、ボードが完全ではないことがわかります。ボードの最後に到達すると、すべての値がわかります。正しいため、これ以上のチェックは必要ありません。
これを行うために、任意の形式のSetを使用できることも誰かが指摘しました。この方法で配置された配列は、小さな連続した固定された数のセットに対して適切に機能する、特に軽量で高性能なセットの形式です。ボードのサイズがわかっている場合は、ビットマスキングを選択することもできますが、効率がそれほど重要ではないことを考えると、少々面倒です。
これはPythonでの私の解決策ですが、これがまだ最短のものであることを嬉しく思います:)
コード:
def check(sud):
zippedsud = Zip(*sud)
boxedsud=[]
for li,line in enumerate(sud):
for box in range(3):
if not li % 3: boxedsud.append([]) # build a new box every 3 lines
boxedsud[box + li/3*3].extend(line[box*3:box*3+3])
for li in range(9):
if [x for x in [set(sud[li]), set(zippedsud[li]), set(boxedsud[li])] if x != set(range(1,10))]:
return False
return True
そして実行:
sudoku=[
[7, 5, 1, 8, 4, 3, 9, 2, 6],
[8, 9, 3, 6, 2, 5, 1, 7, 4],
[6, 4, 2, 1, 7, 9, 5, 8, 3],
[4, 2, 5, 3, 1, 6, 7, 9, 8],
[1, 7, 6, 9, 8, 2, 3, 4, 5],
[9, 3, 8, 7, 5, 4, 6, 1, 2],
[3, 6, 4, 2, 9, 7, 8, 5, 1],
[2, 8, 9, 5, 3, 1, 4, 6, 7],
[5, 1, 7, 4, 6, 8, 2, 3, 9]]
print check(sudoku)
セット(行、列、ボックス)のすべての値をリストに抽出し、並べ替えて、 '(1、2、3、4、5、6、7、8、9)と比較できます。
合計and行/列の乗算が正しい数値45/362880に等しい場合
各セットに9つのセルが含まれるセルセットを作成し、縦の列、横の行、および3x3の正方形のセットを作成します。
次に、各セルについて、それが含まれるセットを識別し、それらを分析します。
これはクラスプロジェクトで1回だけ行いました。合計27セットを使用して、各行、列、ボックスを表しました。各セットに番号を追加するときに番号を確認し(番号を配置するたびに、番号が3つのセット、行、列、ボックスに追加されます)、ユーザーが数字のみを入力したことを確認します1- 9。セットに入力できる唯一の方法は、一意の数字が適切に入力されているかどうかです。 27セットすべてが満たされた場合、パズルは解決されました。ユーザーインターフェイスから27セットへのマッピングの設定は少し面倒ですが、残りのロジックを簡単に実装できました。
少し前に、各行の重複数、各列の重複数、各ボックスの重複数をチェックする数独チェッカーを作成しました。誰かがLinqコードの数行のようなものを思い付くことができれば私はそれが好きです。
char VerifySudoku(char grid[81])
{
for (char r = 0; r < 9; ++r)
{
unsigned int bigFlags = 0;
for (char c = 0; c < 9; ++c)
{
unsigned short buffer = r/3*3+c/3;
// check horizontally
bitFlags |= 1 << (27-grid[(r<<3)+r+c])
// check vertically
| 1 << (18-grid[(c<<3)+c+r])
// check subgrids
| 1 << (9-grid[(buffer<<3)+buffer+r%3*3+c%3]);
}
if (bitFlags != 0x7ffffff)
return 0; // invalid
}
return 1; // valid
}
次のことを確認することは非常に興味深いでしょう。
when the sum of each row/column/box equals n*(n+1)/2
and the product equals n!
with n = number of rows or columns
これは数独のルールで十分です。それはO(n ^ 2)のアルゴリズムを可能にするため、正しいセルを合計して乗算します。
N = 9を見ると、合計は45、製品は362880になります。
あなたは次のようなことをするでしょう:
for i = 0 to n-1 do
boxsum[i] := 0;
colsum[i] := 0;
rowsum[i] := 0;
boxprod[i] := 1;
colprod[i] := 1;
rowprod[i] := 1;
end;
for i = 0 to n-1 do
for j = 0 to n-1 do
box := (i div n^1/2) + (j div n^1/2)*n^1/2;
boxsum[box] := boxsum[box] + cell[i,j];
boxprod[box] := boxprod[box] * cell[i,j];
colsum[i] := colsum[i] + cell[i,j];
colprod[i] := colprod[i] * cell[i,j];
rowsum[j] := colsum[j] + cell[i,j];
rowprod[j] := colprod[j] * cell[i,j];
end;
end;
for i = 0 to n-1 do
if boxsum[i] <> 45
or colsum[i] <> 45
or rowsum[i] <> 45
or boxprod[i] <> 362880
or colprod[i] <> 362880
or rowprod[i] <> 362880
return false;
Pythonでの読みやすい方法は次のとおりです。
from itertools import chain
def valid(puzzle):
def get_block(x,y):
return chain(*[puzzle[i][3*x:3*x+3] for i in range(3*y, 3*y+3)])
rows = [set(row) for row in puzzle]
columns = [set(column) for column in Zip(*puzzle)]
blocks = [set(get_block(x,y)) for x in range(0,3) for y in range(0,3)]
return all(map(lambda s: s == set([1,2,3,4,5,6,7,8,9]), rows + columns + blocks))
それぞれの3x3の正方形はブロックと呼ばれ、3x3のグリッドには9つあります。パズルはリストのリストとして入力され、各内部リストが行であると想定されています。
まず、ブール値を「正しい」にする必要があります。次に、前述のようにforループを作成します。ループとそれ以降のすべて(Javaの場合)のコードは、次のとおりです。fieldは辺が等しい2D配列、colは同じ次元の別の配列、lは1D配列です。
_for(int i=0; i<field.length(); i++){
for(int j=0; j<field[i].length; j++){
if(field[i][j]>9||field[i][j]<1){
checking=false;
break;
}
else{
col[field[i].length()-j][i]=field[i][j];
}
}
}
_
3x3ボックスをチェックする正確なアルゴリズムはわかりませんが、フィールド内のすべての行をチェックし、「/*array name goes here*/[i].contains(1)&&/*array name goes here*/[i].contains(2)
」で行を確認する必要があります(行の長さに達するまで続きます)。 forループ。
def solution(board): for i in board: if sum(i)!= 45: return "Incorrect" for i in range(9): temp2 = [] x for range(9): temp2.append(board [i] [x] ) if sum(temp2)!= 45: return "Incorrect" return "Correct" board = [] for i in range(9): inp = raw_input() temp = [int(i)for i in inp] board.append(temp) print solution(board)
これがCでの私のものです。各正方形を1回だけ通過します。
int checkSudoku(int board[]) {
int i;
int check[13] = { 0 };
for (i = 0; i < 81; i++) {
if (i % 9 == 0) {
check[9] = 0;
if (i % 27 == 0) {
check[10] = 0;
check[11] = 0;
check[12] = 0;
}
}
if (check[i % 9] & (1 << board[i])) {
return 0;
}
check[i % 9] |= (1 << board[i]);
if (check[9] & (1 << board[i])) {
return 0;
}
check[9] |= (1 << board[i]);
if (i % 9 < 3) {
if (check[10] & (1 << board[i])) {
return 0;
}
check[10] |= (1 << board[i]);
} else if (i % 9 < 6) {
if (check[11] & (1 << board[i])) {
return 0;
}
check[11] |= (1 << board[i]);
} else {
if (check[12] & (1 << board[i])) {
return 0;
}
check[12] |= (1 << board[i]);
}
}
}
これが数学教授J.F.クルックによる論文です: 数独パズルを解くための鉛筆と紙のアルゴリズム
このペーパーは2009年4月に発行され、明確な数独ソリューションとして多くの宣伝を得ました(「J.F.Crook数独」についてはgoogleをチェックしてください)。
アルゴリズムの他に、アルゴリズムが機能することの数学的証明もあります(教授は数独をあまり面白くないと認めたので、もっと楽しくするためにいくつかの数学を紙に投げました)。
あなたが行うことができる1つの小さな最適化は、行、列、またはボックスの重複を、O(n ^ 2)ではなくO(n)時間)でチェックできることです。数値のセットは、それぞれをハッシュセットに追加します。言語によっては、実際に一定の時間のルックアップと挿入である真のハッシュセットを使用できる場合があります。次に、同じステップで重複をチェックするかどうかを確認します。挿入は成功したかどうかです。コードのマイナーな改善ですが、O(n ^ 2)からO(n))への移行は、大幅な最適化です。
数独フィールドを受け取り、解決策であればtrue/falseを返す関数を持つインターフェースを作成します。次に、制約ごとに単一の検証クラスとして制約を実装します。
すべての制約クラスを繰り返し、すべてが合格したときに数独が正しいことを確認するだけです。高速化するには、失敗する可能性が最も高いものを前に置き、無効なフィールドを指す最初の結果で停止します。
かなり一般的なパターン。 ;-)
もちろん、これを拡張して、どのフィールドがおそらく間違っているかなどのヒントを提供できます。
最初の制約。すべてのフィールドが入力されているかどうかを確認してください。 (単純なループ)2番目のチェックですべての数値が各ブロックにあるかどうか(ネストされたループ)3番目のチェックで完全な行と列(上記とほぼ同じ手順ですが、アクセス方式が異なります)
array = [1,2,3,4,5,6,7,8,9]
sudoku = int [][]
puzzle = 9 #9x9
columns = map []
units = map [] # box
unit_l = 3 # box width/height
check_puzzle()
def strike_numbers(line, line_num, columns, units, unit_l):
count = 0
for n in line:
# check which unit we're in
unit = ceil(n / unit_l) + ceil(line_num / unit_l) # this line is wrong - rushed
if units[unit].contains(n): #is n in unit already?
return columns, units, 1
units[unit].add(n)
if columns[count].contains(n): #is n in column already?
return columns, units, 1
columns[count].add(n)
line.remove(n) #remove num from temp row
return columns, units, line.length # was a number not eliminated?
def check_puzzle(columns, sudoku, puzzle, array, units):
for (i=0;i< puzzle;i++):
columns, units, left_over = strike_numbers(sudoku[i], i, columns, units) # iterate through rows
if (left_over > 0): return false
徹底的にチェックしなくても、2回ループするだけで問題なく動作します(少しのデバッグが必要)。 O(3(n ^ 2))の代わりにO(n ^ 2)
Int sudoku [0..8,0..8]が数独フィールドであるとしましょう。
bool CheckSudoku(int[,] sudoku)
{
int flag = 0;
// Check rows
for(int row = 0; row < 9; row++)
{
flag = 0;
for (int col = 0; col < 9; col++)
{
// edited : check range step (see comments)
if ((sudoku[row, col] < 1)||(sudoku[row, col] > 9))
{
return false;
}
// if n-th bit is set.. but you can use a bool array for readability
if ((flag & (1 << sudoku[row, col])) != 0)
{
return false;
}
// set the n-th bit
flag |= (1 << sudoku[row, col]);
}
}
// Check columns
for(int col= 0; col < 9; col++)
{
flag = 0;
for (int row = 0; row < 9; row++)
{
if ((flag & (1 << sudoku[row, col])) != 0)
{
return false;
}
flag |= (1 << sudoku[row, col]);
}
}
// Check 3x3 boxes
for(int box= 0; box < 9; box++)
{
flag = 0;
for (int ofs = 0; ofs < 9; ofs++)
{
int col = (box % 3) * 3;
int row = ((int)(box / 3)) * 3;
if ((flag & (1 << sudoku[row, col])) != 0)
{
return false;
}
flag |= (1 << sudoku[row, col]);
}
}
return true;
}
これは私がこれのためにやったことです:
boolean checkers=true;
String checking="";
if(a.length/3==1){}
else{
for(int l=1; l<a.length/3; l++){
for(int n=0;n<3*l;n++){
for(int lm=1; lm<a[n].length/3; lm++){
for(int m=0;m<3*l;m++){
System.out.print(" "+a[n][m]);
if(a[n][m]<=0){
System.out.print(" (Values must be positive!) ");
}
if(n==0){
if(m!=0){
checking+=", "+a[n][m];
}
else{
checking+=a[n][m];
}
}
else{
checking+=", "+a[n][m];
}
}
}
System.out.print(" "+checking);
System.out.println();
}
}
for (int i=1;i<=a.length*a[1].length;i++){
if(checking.contains(Integer.toString(i))){
}
else{
checkers=false;
}
}
}
checkers=checkCol(a);
if(checking.contains("-")&&!checking.contains("--")){
checkers=false;
}
System.out.println();
if(checkers==true){
System.out.println("This is correct! YAY!");
}
else{
System.out.println("Sorry, it's not right. :-(");
}
}
private static boolean checkCol(int[][]a){
boolean checkers=true;
int[][]col=new int[][]{{0,0,0},{0,0,0},{0,0,0}};
for(int i=0; i<a.length; i++){
for(int j=0; j<a[i].length; j++){
if(a[i][j]>9||a[i][j]<1){
checkers=false;
break;
}
else{
col[a[i].length-j][i]=a[i][j];
}
}
}
String alia="";
for(int i=0; i<col.length; i++){
for(int j=1; j<=col[i].length; j++){
alia=a[i].toString();
if(alia.contains(""+j)){
alia=col[i].toString();
if(alia.contains(""+j)){}
else{
checkers=false;
}
}
else{
checkers=false;
}
}
}
return checkers;
}
数独が解決されたかどうかは、次の2つの方法で確認できます。
素朴な解決策は、すべての正方形の谷を反復し、その数が占める行、列ブロックでその数が一意であるかどうかを確認することです。
しかし、もっと良い方法があります。
これは、すべての数値に対して行うのではなく、すべての行、列、およびブロックをチェックするだけで済みます。簡単な実装は、1から9までの数字のビットフィールドを持ち、列、行、ブロックを繰り返すときにそれらを削除することです。不足している数値を削除しようとした場合、または終了時にフィールドが空でない場合、数独は正しく解決されません。
これはSwiftの非常に簡潔なバージョンで、Intの配列を使用して9つの数値のグループを追跡するだけで、数独を1回だけ反復します。
import UIKit
func check(_ sudoku:[[Int]]) -> Bool {
var groups = Array(repeating: 0, count: 27)
for x in 0...8 {
for y in 0...8 {
groups[x] += 1 << sudoku[x][y] // Column (group 0 - 8)
groups[y + 9] += 1 << sudoku[x][y] // Row (group 9 - 17)
groups[(x + y * 9) / 9 + 18] += 1 << sudoku[x][y] // Box (group 18 - 27)
}
}
return groups.filter{ $0 != 1022 }.count == 0
}
let sudoku = [
[7, 5, 1, 8, 4, 3, 9, 2, 6],
[8, 9, 3, 6, 2, 5, 1, 7, 4],
[6, 4, 2, 1, 7, 9, 5, 8, 3],
[4, 2, 5, 3, 1, 6, 7, 9, 8],
[1, 7, 6, 9, 8, 2, 3, 4, 5],
[9, 3, 8, 7, 5, 4, 6, 1, 2],
[3, 6, 4, 2, 9, 7, 8, 5, 1],
[2, 8, 9, 5, 3, 1, 4, 6, 7],
[5, 1, 7, 4, 6, 8, 2, 3, 9]
]
if check(sudoku) {
print("Pass")
} else {
print("Fail")
}
あなたのボードが1-nから行くと仮定しましょう。
検証配列を作成し、それを埋めてから検証します。
grid [0-(n-1)][0-(n-1)]; //this is the input grid
//each verification takes n^2 bits, so three verifications gives us 3n^2
boolean VArray (3*n*n) //make sure this is initialized to false
for i = 0 to n
for j = 0 to n
/*
each coordinate consists of three parts
row/col/box start pos, index offset, val offset
*/
//to validate rows
VArray( (0) + (j*n) + (grid[i][j]-1) ) = 1
//to validate cols
VArray( (n*n) + (i*n) + (grid[i][j]-1) ) = 1
//to validate boxes
VArray( (2*n*n) + (3*(floor (i/3)*n)+ floor(j/3)*n) + (grid[i][j]-1) ) = 1
next
next
if every array value is true then the solution is correct.
私はそこにいくつかの愚かな間違いをしたと確信していますが、私はそれでうまくいくと思います。ボートに完全に乗り遅れたかもしれません。