构建最小线索数独解算器


16

我试图说明这个问题,但提出了更为客观的解决标准。

您的任务是构建一个程序或函数,该程序或函数采用S您选择的格式的已解决的Sudoku网格,并尝试生成具有S唯一解决方案的尽可能少线索的问题网格。(S只要解决方案证明是唯一的,什么方法是唯一的解决方案,包括蛮力都没有关系。)


通过在此文件中找到的一组100,000个解决方案网格中运行该程序,可以对您的程序进行评分(下载7.82 MB),并将解决方案产生的所有100,000个问题网格中的线索总数相加。

上面测试文件中的Sudoku解决方案从左到右,然后从上到下,以81个字符的字符串表示。将测试文件中的输入转换为可用解决方案所需的代码不会计入解决方案的字节数。

就像在我的Flood Paint挑战中一样,您的程序实际上必须为所有100,000个难题产生有效的输出,才能将其视为有效的解决方案。在所有100,000个测试案例中,输出总线索最少的程序是赢家,而较短的代码打破了平局。


当前记分牌:

  1. 2,361,024 -Nutki,C
  2. 2,580,210 -es1024,PHP
  3. 6,000,000 -CarpetPython,Python 2
  4. 720万-Joe Z.,Python

另外,您可以肯定任何主张少于1,700,000个解决方案的解决方案都是假的,但是我想看看这些解决方案能达到多低的水平。
Joe Z. 2015年

Answers:


8

C-2,361,024 2,509,949线索

如果蛮力求解器仅找到一个唯一的解决方案,请从最后一个单元格开始删除线索。

第二次尝试:使用启发式方法确定删除线索的顺序,而不是从上一个开始。这使代码运行慢得多(20分钟而不是2分钟来计算结果)。我可以使求解器更快,以尝试不同的启发式方法,但是现在可以了。

#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条线索

与往常一样,这是最后一个参考解决方案:

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

在所有情况下,删除底行的数字都可解决难题,因为每列仍填充9个数字中的8个,并且底行中的每个数字仅是列中剩余的第九个数字。

如果有任何认真的竞争者在法律上取得的成绩比这差,我会感到惊讶。


我的意思是,您可以只删除最后一个。
Seequ 2015年

您也可以将整个问题解决,但是这些都不是真正的竞争者。
Joe Z.

那么,为什么这是一个认真的竞争者呢?
theonlygusti 2015年

不是。这就是为什么我说,如果有任何认真的竞争者取得比这个不认真的竞争者更糟糕的成绩,我会感到惊讶。
Joe Z.

1

Python 2-6,000,000条线索

一个简单的解决方案,使用3种常见方法解决这些难题:

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

此函数产生如下线索格式:

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

这总是可以解决的。首先求解4个3x3部分,然后求解8列,然后求解9行。


1

PHP — 2,580,210线索

首先删除每个框的最后一行和最后一列以及右下角。然后,它尝试清除每个单元,并在每次更改后通过一个简单的求解器运行该电路板,以确保该电路板仍可明确地求解。

下面的许多代码是从我的旧答案之一修改而来printBoard对空单元格使用0。

<?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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.