Construire un résolveur de Sudoku à indice minimum

16

Ma tentative de poser cette question , mais avec un critère de résolution plus objectif.

Votre tâche consiste à créer un programme ou une fonction qui prend une grille de Sudoku résolue Sdans le format de votre choix et tente de générer une grille de problème avec le moins d'indices possible qui a Spour solution unique. (Peu importe quelle méthode Sest la solution unique, y compris la force brute, tant que la solution est prouvée unique.)


Votre programme sera noté en l'exécutant à travers un ensemble de 100 000 grilles de solutions trouvées dans ce fichier (7,82 Mo de téléchargement) et en additionnant le nombre d'indices dans les 100 000 grilles de problèmes produites par votre solution.

Les solutions Sudoku du fichier de test ci-dessus sont exprimées sous la forme d'une chaîne de 81 caractères, de gauche à droite, puis de haut en bas. Le code requis pour transformer l'entrée du fichier de test en une solution utilisable ne comptera pas pour le nombre d'octets de votre solution.

Comme dans mon défi Flood Paint , votre programme doit réellement produire une sortie valide pour les 100 000 puzzles pour être considéré comme une solution valide. Le programme qui génère le moins d'indices au total pour les 100 000 cas de test est le gagnant, avec un code plus court qui rompt une égalité.


Tableau de bord actuel:

  1. 2,361,024 - nutki, C
  2. 2,580,210 - es1024, PHP
  3. 6 000 000 - CarpetPython, Python 2
  4. 7 200 000 - Joe Z., Python
Joe Z.
la source
En outre, vous pouvez être sûr que toute solution revendiquant moins de 1 700 000 solutions est fausse, mais je veux voir à quel point celles-ci peuvent descendre.
Joe Z.

Réponses:

8

C - 2 361 024 2 509 949 indices

Supprimez les indices à partir de la dernière cellule si un solveur de force brute ne trouve qu'une seule solution unique.

Deuxième essai: utilisez l'heuristique pour décider dans quel ordre supprimer les indices au lieu de partir du dernier. Cela rend le code exécuté beaucoup plus lentement (20 minutes au lieu de 2 pour calculer le résultat). Je pourrais rendre le solveur plus rapide, pour expérimenter différentes heuristiques, mais pour l'instant, cela suffira.

#include <stdio.h>
#include <string.h>
char ll[100];
short b[81];
char m[81];
char idx[81][24];
int s;
char lg[513];
void pri2() {
    int i;
    for(i=0;i<81;i++) putchar(lg[b[i]]);
    putchar('\n');
}
void solve(pos){
int i,p;
if (s > 1) return;
if (pos == 81) { s++; return; }
if (b[pos]) return solve(pos+1);
for (p=i=0;i<24;i++) p |= b[idx[pos][i]];
for (i = 0; i < 9; i++) if (!(p&(1<<i))) {
    b[pos] = 1 << i;
    solve(pos + 1);
}
b[pos] = 0;
}
int main() {
    int i,j,t;
    for(i=0;i<9;i++) lg[1<<i]='1'+i;
    lg[0] = '.';
    for(i=0;i<81;i++) {
    t = 0;
    for(j=0;j<9;j++) if(i/9*9 + j != i) idx[i][t++] = i/9*9 + j;
    for(j=0;j<9;j++) if(i%9 + j*9 != i) idx[i][t++] = i%9 + j*9;
    for(j=0;j<81;j++) if(j/27 == i/27 && i%9/3 == j%9/3 && i!=j) idx[i][t++] = j;
    }
    while(scanf("%s ",ll)>0) {
    memset(m, 0, sizeof(m));
    for(i=0;i<81;i++) b[i] = 1 << (ll[i]-'1');
    for(i=0;i<81;i++) {
    int j,k,l = 99;
    for(k=0;k<81;k++) if (m[k] <= l) l = m[k], j = k;
    m[j] = 24;
    t = b[j]; b[j] = 0;
    s = 0; solve(0);
    if (s > 1) b[j] = t;
    else for(k=0;k<24;k++) m[idx[j][k]]++;
    }
    pri2();
    }
    return 0;
}
nutki
la source
1

Python - 7 200 000 indices

Comme d'habitude, voici une solution de référence de dernière place:

def f(x): return x[:72] + "." * 9

La suppression de la rangée inférieure de chiffres laisse prouvablement le casse-tête résolu dans tous les cas, car chaque colonne contient toujours 8 des 9 numéros, et chaque numéro de la rangée inférieure est simplement le neuvième numéro restant dans la colonne.

Si un concurrent sérieux réussit à obtenir légalement un score inférieur à celui-ci, je serai étonné.

Joe Z.
la source
Je veux dire, vous pouvez supprimer juste le dernier.
seequ
Vous pouvez également laisser le problème résolu, mais aucun de ceux-ci ne serait un concurrent sérieux.
Joe Z.
Alors, pourquoi est-ce un concurrent sérieux?
theonlygusti
Ce n'est pas. C'est pourquoi j'ai dit que je serais étonné si un concurrent sérieux réussissait à obtenir un score pire que ce concurrent non sérieux.
Joe Z.
1

Python 2 - 6 000 000 d'indices

Une solution simple qui utilise les 3 méthodes courantes pour résoudre ces énigmes:

def f(x): 
    return ''.join('.' if i<9 or i%9==0 or (i+23)%27 in (0,3) else c 
        for i,c in enumerate(x))

Cette fonction produit des formats d'indices comme celui-ci:

.........
.dddddddd
.dddddddd
.ddd.dd.d
.dddddddd
.dddddddd
.ddd.dd.d
.dddddddd
.dddddddd

Cela peut toujours être résolu. Les 4 parties 3x3 sont résolues en premier, puis les 8 colonnes, puis les 9 lignes.

Logic Knight
la source
1

PHP - 2 580 210 indices

Cela supprime d'abord la dernière ligne et colonne, et le coin inférieur droit de chaque boîte. Il essaie ensuite d'effacer chaque cellule, en exécutant la carte à travers un simple solveur après chaque changement pour garantir que la carte est toujours résolue sans ambiguïté.

Une grande partie du code ci-dessous a été modifié à partir de l' une de mes anciennes réponses . printBoardutilise 0 pour les cellules vides.

<?php
// checks each row/col/block and removes impossible candidates
function reduce($cand){
    do{
        $old = $cand;
        for($r = 0; $r < 9; ++$r){
        for($c = 0; $c < 9; ++$c){
            if(count($cand[$r][$c]) == 1){ // if filled in
                // remove values from row and col and block
                $remove = $cand[$r][$c];
                for($i = 0; $i < 9; ++$i){
                    $cand[$r][$i] = array_diff($cand[$r][$i],$remove);
                    $cand[$i][$c] = array_diff($cand[$i][$c],$remove);
                    $br = floor($r/3)*3+$i/3;
                    $bc = floor($c/3)*3+$i%3;
                    $cand[$br][$bc] = array_diff($cand[$br][$bc],$remove);
                }
                $cand[$r][$c] = $remove;
            }
        }}
    }while($old != $cand);
    return $cand;
}

// checks candidate list for completion
function done($cand){
    for($r = 0; $r < 9; ++$r){
    for($c = 0; $c < 9; ++$c){
        if(count($cand[$r][$c]) != 1)
            return false;
    }}
    return true;
}

// board format: [[1,2,0,3,..],[..],..], $b[$row][$col]
function solve($board){
    $cand = [[],[],[],[],[],[],[],[],[]];
    for($r = 0; $r < 9; ++$r){
    for($c = 0; $c < 9; ++$c){
        if($board[$r][$c]){ // if filled in
            $cand[$r][$c] = [$board[$r][$c]];
        }else{
            $cand[$r][$c] = range(1, 9);
        }
    }}
    $cand = reduce($cand);

    if(done($cand))  // goto not really necessary
        goto end;    // but it feels good to use it 
    else return false;

    end:
    // back to board format
    $b = [];
    for($r = 0; $r < 9; ++$r){
        $b[$r] = [];
        for($c = 0; $c < 9; ++$c){
            if(count($cand[$r][$c]) == 1)
                $b[$r][$c] = array_pop($cand[$r][$c]);
            else 
                $b[$r][$c] = 0;
        }
    }
    return $b;
}

function add_zeros($board, $ind){
    for($r = 0; $r < 9; ++$r){
    for($c = 0; $c < 9; ++$c){
        $R = ($r + (int)($ind/9)) % 9;
        $C = ($c + (int)($ind%9)) % 9;
        if($board[$R][$C]){
            $tmp = $board[$R][$C];
            $board[$R][$C] = 0;
            if(!solve($board))
                $board[$R][$C] = $tmp;
        }   
    }}
    return $board;
}

function generate($board, $ind){
    // remove last row+col
    $board[8] = [0,0,0,0,0,0,0,0,0];
    foreach($board as &$j) $j[8] = 0;

    // remove bottom corner of each box
    $board[2][2] = $board[2][5] = $board[5][2] = $board[5][5] = 0;

    $board = add_zeros($board, $ind);

    return $board;    
}
function countClues($board){
    $str = implode(array_map('implode', $board));
    return 81 - substr_count($str, '0');
}

function generateBoard($board){
    return generate($board, 0);
}

function printBoard($board){
    for($i = 0; $i < 9; ++$i){
        echo implode(' ', $board[$i]) . PHP_EOL;
    }
    flush();
}
function readBoard($str){
    $tmp = str_split($str, 9);
    $board = [];
    for($i = 0; $i < 9; ++$i)
        $board[] = str_split($tmp[$i], 1);
    return $board;
}
// testing
$n = 0;
$f = fopen('ppcg_sudoku_testing.txt', 'r');
while(($l = fgets($f)) !== false){
    $board = readBoard(trim($l));
    $n += countClues(generateBoard($board));
}
echo $n;
es1024
la source