Costruisci un risolutore di Sudoku con la minima idea


16

Il mio tentativo di affermare questa domanda , ma con un criterio di risoluzione più oggettivo.

Il tuo compito è costruire un programma o una funzione che prende una griglia di Sudoku risolta Snel formato che preferisci e tenta di generare una griglia di problemi con il minor numero di indizi possibile che abbia Scome unica soluzione. (Non importa quale metodo Ssia la soluzione unica, inclusa la forza bruta, purché la soluzione sia dimostrabilmente unica.)


Il tuo programma verrà valutato eseguendolo attraverso una serie di 100.000 griglie di soluzioni trovate in questo file (7,82 MB di download) e sommando il numero di indizi in tutte le 100.000 griglie di problemi prodotte dalla tua soluzione.

Le soluzioni di Sudoku nel file di test sopra sono espresse come una stringa di 81 caratteri, da sinistra a destra, quindi dall'alto verso il basso. Il codice richiesto per trasformare l'input nel file di test in una soluzione utilizzabile non verrà conteggiato nel conteggio dei byte della soluzione.

Come nella mia sfida Flood Paint , il tuo programma deve effettivamente produrre un output valido per tutti i 100.000 puzzle per essere considerato una soluzione valida. Il programma che fornisce il minor numero di indizi per tutti i 100.000 casi di test è il vincitore, con un codice più breve che rompe un pareggio.


Quadro di valutazione attuale:

  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

Inoltre, puoi essere sicuro che qualsiasi soluzione che richieda meno di 1.700.000 soluzioni è un falso, ma voglio vedere quanto possono andare in basso.
Joe Z.

Risposte:


8

C - 2.361.024 2.509.949 indizi

Rimuovi gli indizi a partire dall'ultima cella se un solutore di forza bruta trova solo una soluzione unica.

Secondo tentativo: usa l'euristica per decidere in quale ordine rimuovere gli indizi invece di partire dall'ultimo. Questo rende il codice molto più lento (20 minuti anziché 2 per calcolare il risultato). Potrei rendere il risolutore più veloce, sperimentare diverse euristiche, ma per ora lo farà.

#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;
}

1

Python - 7.200.000 indizi

Come al solito, ecco una soluzione di riferimento all'ultimo posto:

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

La rimozione della riga inferiore di numeri lascia risolvibile il puzzle in tutti i casi, poiché ogni colonna ha ancora 8 di 9 numeri riempiti e ogni numero nella riga inferiore è semplicemente il nono numero rimasto nella colonna.

Se un concorrente serio riesce a segnare legalmente un punteggio peggiore di questo, rimarrò stupito.


Voglio dire, potresti rimuovere solo l'ultimo.
Seequ,

Potresti anche lasciare tutto risolto, ma nessuno di questi sarebbe un serio contendente.
Joe Z.

Allora perché questo è un serio contendente?
theonlygusti

Non è. Ecco perché ho detto che sarei stupito se un concorrente serio fosse riuscito a segnare peggio di questo contendente non serio.
Joe Z.

1

Python 2 - 6.000.000 di indizi

Una soluzione semplice che utilizza i 3 metodi comuni per risolvere questi puzzle:

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

Questa funzione produce formati di indizio come questo:

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

Questo può sempre essere risolto. Vengono risolte prima le 4 parti 3x3, quindi le 8 colonne, quindi le 9 righe.


1

PHP - 2.580.210 indizi

In questo modo vengono rimosse l'ultima riga e colonna e l'angolo in basso a destra di ogni riquadro. Quindi cerca di cancellare ogni cella, facendo scorrere la scheda attraverso un semplice risolutore dopo ogni modifica per garantire che la scheda sia ancora inequivocabilmente risolvibile.

Gran parte del codice seguente è stato modificato da una delle mie vecchie risposte . printBoardusa 0s per celle vuote.

<?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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.