Xây dựng một trình hủy bỏ Sudoku đầu mối tối thiểu


16

Nỗ lực của tôi trong việc nêu câu hỏi này , nhưng với một tiêu chí giải quyết khách quan hơn.

Nhiệm vụ của bạn là xây dựng một chương trình hoặc chức năng lấy lưới Sudoku đã giải quyết Stheo định dạng bạn chọn và cố gắng tạo ra một lưới vấn đề với càng ít manh mối càng tốt có Sgiải pháp duy nhất. (Không quan trọng phương pháp nào Slà giải pháp duy nhất bằng cách, bao gồm cả lực lượng vũ phu, miễn là giải pháp đó là duy nhất có thể chứng minh được.)


Chương trình của bạn sẽ được ghi điểm bằng cách chạy nó thông qua bộ 100.000 lưới giải pháp được tìm thấy trong tệp này (tải xuống 7,82 MB) và thêm số lượng manh mối trong tất cả 100.000 lưới vấn đề mà giải pháp của bạn tạo ra.

Các giải pháp Sudoku trong tệp thử nghiệm ở trên được thể hiện dưới dạng chuỗi 81 ký tự, từ trái sang phải, sau đó từ trên xuống dưới. Mã được yêu cầu để biến đầu vào trong tệp thử nghiệm thành một giải pháp có thể sử dụng sẽ không được tính vào số byte của giải pháp của bạn.

Như trong thử thách Flood Paint của tôi , chương trình của bạn thực sự phải tạo ra một đầu ra hợp lệ cho tất cả 100.000 câu đố để được coi là một giải pháp hợp lệ. Chương trình đưa ra tổng số manh mối ít nhất cho tất cả 100.000 trường hợp thử nghiệm là người chiến thắng, với mã ngắn hơn phá vỡ một mối quan hệ.


Bảng điểm hiện tại:

  1. 2,361,024 - nutki, C
  2. 2.580.210 - es1024, PHP
  3. 6.000.000 - RugPython, Python 2
  4. 7.200.000 - Joe Z., Python

Ngoài ra, bạn có thể chắc chắn rằng bất kỳ giải pháp nào yêu cầu ít hơn 1.700.000 giải pháp đều là giả mạo, nhưng tôi muốn xem những giải pháp này có thể đi thấp đến mức nào.
Joe Z.

Câu trả lời:


8

C - 2.361.024 2.509.949 manh mối

Xóa các đầu mối bắt đầu từ ô cuối cùng nếu người giải quyết lực lượng vũ phu chỉ tìm thấy một giải pháp duy nhất.

Thử lần thứ hai: sử dụng heuristic để quyết định theo thứ tự nào để loại bỏ manh mối thay vì bắt đầu từ lần cuối. Điều này làm cho mã chạy chậm hơn nhiều (20 phút thay vì 2 để tính kết quả). Tôi có thể làm cho người giải nhanh hơn, để thử nghiệm các phương pháp phỏng đoán khác nhau, nhưng bây giờ nó sẽ làm được.

#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 manh mối

Như thường lệ, đây là một giải pháp tham khảo cuối cùng:

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

Việc xóa hàng dưới cùng của các số có thể giải được câu đố có thể giải được trong mọi trường hợp, vì mỗi cột vẫn còn 8 trong số 9 số được điền và mỗi số ở hàng dưới chỉ đơn giản là số thứ chín còn lại trong cột.

Nếu bất kỳ ứng cử viên nghiêm túc nào đạt được điểm số hợp pháp tệ hơn người này, tôi sẽ ngạc nhiên.


Ý tôi là, bạn có thể loại bỏ chỉ cái cuối cùng.
xem

Bạn cũng có thể để lại toàn bộ vấn đề đã được giải quyết, nhưng cả hai sẽ không phải là một ứng cử viên nghiêm trọng.
Joe Z.

Vậy tại sao đây là một ứng cử viên nghiêm trọng?
theonlygusti

Nó không thể. Đó là lý do tại sao tôi nói rằng tôi sẽ ngạc nhiên nếu bất kỳ ứng cử viên nghiêm túc nào có thể ghi điểm kém hơn đối thủ không nghiêm trọng này.
Joe Z.

1

Python 2 - 6.000.000 manh mối

Một giải pháp đơn giản sử dụng 3 phương pháp phổ biến để giải các câu đố này:

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

Hàm này tạo ra các định dạng đầu mối như thế này:

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

Điều này luôn có thể được giải quyết. 4 phần 3x3 được giải trước, sau đó là 8 cột, sau đó là 9 hàng.


1

PHP - 2.580.210 đầu mối

Điều này đầu tiên loại bỏ hàng và cột cuối cùng, và góc dưới cùng bên phải của mỗi hộp. Sau đó, nó cố gắng xóa từng ô, chạy bảng thông qua một bộ giải đơn giản sau mỗi thay đổi để đảm bảo bảng vẫn có thể giải quyết rõ ràng.

Phần lớn mã dưới đây đã được sửa đổi từ một trong những câu trả lời cũ của tôi . printBoardsử dụng 0s cho các ô trống.

<?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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.