web-dev-qa-db-ja.com

数独フィールドをチェックするためのクールなアルゴリズム?

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

しかし、私は(よりエレガントな意味で)より良い解決策があるに違いないと確信しています。効率は非常に重要ではありません。

25
user18670

あなたは数独のすべての制約をチェックする必要があります:

  • 各行の合計を確認してください
  • 各列の合計を確認してください
  • 各ボックスの合計を確認します
  • 各行の重複する番号を確認します
  • 各列の重複する番号を確認します
  • 各ボックスの番号が重複していないか確認してください

これは6つのチェックです。総当たりのアプローチを使用しています。

ボードのサイズ(3x3または9x9)がわかっている場合は、ある種の数学的最適化を使用できます。

編集:合計の制約の説明:最初に合計を確認する(合計が45でない場合は停止する)ことは、重複を確認するよりもはるかに高速(かつ簡単)です。間違ったソリューションを破棄する簡単な方法を提供します。

22
Radu094

Peter Norvigが数独パズルを(pythonで)解決することについての素晴らしい記事を持っています、

http://norvig.com/sudoku.html

多分それはあなたがやりたいことには多すぎますが、とにかくそれは素晴らしい読書です

23
daniel

ちょっと考えてみます。各3x3の正方形の数字もチェックする必要はありませんか?

正しい数独がなくても行と列の条件を満たすことができるかどうかを考えています

7
Luk

各行、列、ボックスをチェックして、重複しないようにそれぞれ1〜9の数字が含まれるようにします。ここでのほとんどの回答はすでにこれについて議論しています。

しかし、それを効率的に行う方法は?回答:次のようなループを使用してください

result=0;
for each entry:
  result |= 1<<(value-1)
return (result==511);

各数値は結果の1ビットを設定します。 9つの数値すべてが一意である場合、最下位の9ビットが設定されます。したがって、「重複のチェック」テストは、9ビットすべてが設定されていることのチェックにすぎません。これは、テスト結果== 511と同じです。これらのチェックのうち27行を行う必要があります。行、列、ボックスごとに1つずつです。

7
SPWorley

すべての行、列、正方形のブール値の配列を作成します。配列のインデックスは、その行、列、または正方形に配置されたvalueを表します。つまり、2番目の行の最初の列に5を追加する場合、rows [2] [5]をtrueに設定し、さらにcolumn [1] [5]とsquares [4] [5]を設定して、行、列、正方形に5値。

元のボードがどのように表現されているかに関係なく、これは、完全性と正確性を確認するためのシンプルで非常に高速な方法です。ボードに表示されている順に番号を取得し、このデータ構造の構築を開始します。ボードに数値を配置すると、O(1)演算になり、特定の行、列、または四角形で値が重複しているかどうかを判断します(また、各値が正当な数値であることを確認します。空白または高すぎる数値を示した場合、ボードが完全ではないことがわかります。ボードの最後に到達すると、すべての値がわかります。正しいため、これ以上のチェックは必要ありません。

これを行うために、任意の形式のSetを使用できることも誰かが指摘しました。この方法で配置された配列は、小さな連続した固定された数のセットに対して適切に機能する、特に軽量で高性能なセットの形式です。ボードのサイズがわかっている場合は、ビットマスキングを選択することもできますが、効率がそれほど重要ではないことを考えると、少々面倒です。

4

これは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)        
4
Kami

セット(行、列、ボックス)のすべての値をリストに抽出し、並べ替えて、 '(1、2、3、4、5、6、7、8、9)と比較できます。

2
Svante

合計and行/列の乗算が正しい数値45/362880に等しい場合

2
user189317

各セットに9つのセルが含まれるセルセットを作成し、縦の列、横の行、および3x3の正方形のセットを作成します。

次に、各セルについて、それが含まれるセットを識別し、それらを分析します。

これはクラスプロジェクトで1回だけ行いました。合計27セットを使用して、各行、列、ボックスを表しました。各セットに番号を追加するときに番号を確認し(番号を配置するたびに、番号が3つのセット、行、列、ボックスに追加されます)、ユーザーが数字のみを入力したことを確認します1- 9。セットに入力できる唯一の方法は、一意の数字が適切に入力されているかどうかです。 27セットすべてが満たされた場合、パズルは解決されました。ユーザーインターフェイスから27セットへのマッピングの設定は少し面倒ですが、残りのロジックを簡単に実装できました。

2
Bill the Lizard

少し前に、各行の重複数、各列の重複数、各ボックスの重複数をチェックする数独チェッカーを作成しました。誰かが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
}
1
Hao Wooi Lim

次のことを確認することは非常に興味深いでしょう。

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;
1

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つあります。パズルはリストのリストとして入力され、各内部リストが行であると想定されています。

1
bjrnt

まず、ブール値を「正しい」にする必要があります。次に、前述のように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ループ。

1
user2425429
 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)
 
0

これが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]);
    }
  }
}
0
jpiasetz

これが数学教授J.F.クルックによる論文です: 数独パズルを解くための鉛筆と紙のアルゴリズム

このペーパーは2009年4月に発行され、明確な数独ソリューションとして多くの宣伝を得ました(「J.F.Crook数独」についてはgoogleをチェックしてください)。

アルゴリズムの他に、アルゴリズムが機能することの数学的証明もあります(教授は数独をあまり面白くないと認めたので、もっと楽しくするためにいくつかの数学を紙に投げました)。

0
zendar

あなたが行うことができる1つの小さな最適化は、行、列、またはボックスの重複を、O(n ^ 2)ではなくO(n)時間)でチェックできることです。数値のセットは、それぞれをハッシュセットに追加します。言語によっては、実際に一定の時間のルックアップと挿入である真のハッシュセットを使用できる場合があります。次に、同じステップで重複をチェックするかどうかを確認します。挿入は成功したかどうかです。コードのマイナーな改善ですが、O(n ^ 2)からO(n))への移行は、大幅な最適化です。

0
IceMetalPunk

数独フィールドを受け取り、解決策であればtrue/falseを返す関数を持つインターフェースを作成します。次に、制約ごとに単一の検証クラスとして制約を実装します。

すべての制約クラスを繰り返し、すべてが合格したときに数独が正しいことを確認するだけです。高速化するには、失敗する可能性が最も高いものを前に置き、無効なフィールドを指す最初の結果で停止します。

かなり一般的なパターン。 ;-)

もちろん、これを拡張して、どのフィールドがおそらく間違っているかなどのヒントを提供できます。

最初の制約。すべてのフィールドが入力されているかどうかを確認してください。 (単純なループ)2番目のチェックですべての数値が各ブロックにあるかどうか(ネストされたループ)3番目のチェックで完全な行と列(上記とほぼ同じ手順ですが、アクセス方式が異なります)

0
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)

0
Josh Smeaton

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;

}

0
Marco M.

これは私がこれのためにやったことです:

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;
}
0
user2425429

数独が解決されたかどうかは、次の2つの方法で確認できます。

  • 番号が各行、列、ブロックで一意であるかどうかを確認します。

素朴な解決策は、すべての正方形の谷を反復し、その数が占める行、列ブロックでその数が一意であるかどうかを確認することです。

しかし、もっと良い方法があります。

  • 数独は、すべての行、列、ブロックに数値の順列が含まれている場合に解決されます(1〜9)

これは、すべての数値に対して行うのではなく、すべての行、列、およびブロックをチェックするだけで済みます。簡単な実装は、1から9までの数字のビットフィールドを持ち、列、行、ブロックを繰り返すときにそれらを削除することです。不足している数値を削除しようとした場合、または終了時にフィールドが空でない場合、数独は正しく解決されません。

0
this

これは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")
}
0
Nick Locking

あなたのボードが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. 

私はそこにいくつかの愚かな間違いをしたと確信していますが、私はそれでうまくいくと思います。ボートに完全に乗り遅れたかもしれません。

0
Bryan