สร้างซูโดกุ unsolver ขั้นต่ำ


16

ความพยายามของฉันในการระบุคำถามนี้แต่มีเกณฑ์การแก้ปัญหาที่เป็นเป้าหมายมากกว่า

งานของคุณคือการสร้างโปรแกรมหรือฟังก์ชั่นที่ใช้กริด Sudoku ที่ได้รับการแก้ไขSในรูปแบบที่คุณเลือกและพยายามที่จะสร้างกริดปัญหาที่มีปมน้อยที่สุดเท่าที่จะทำได้ซึ่ง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. 7,200,000 - Joe Z. , Python

นอกจากนี้คุณสามารถมั่นใจได้ว่าโซลูชันที่อ้างสิทธิ์น้อยกว่า 1,700,000 โซลูชั่นนั้นเป็นของปลอม แต่ฉันต้องการดูว่ามันจะไปได้น้อยแค่ไหน
Joe Z.

คำตอบ:


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

การลบแถวด้านล่างของตัวเลขออกไปอย่างชัดเจนทำให้ไขปริศนาแก้ปัญหาได้ในทุกกรณีเนื่องจากแต่ละคอลัมน์ยังคงมีจำนวน 8 จาก 9 หมายเลขและแต่ละหมายเลขในแถวด้านล่างเป็นเพียงหมายเลขเก้าที่เหลืออยู่ในคอลัมน์

หากผู้เข้าแข่งขันที่ร้ายแรงสามารถทำคะแนนได้แย่กว่าผู้เล่นคนนี้อย่างถูกกฎหมายฉันจะประหลาดใจ


ฉันหมายความว่าคุณสามารถลบอันสุดท้ายได้
seequ

คุณสามารถทิ้งทุกสิ่งไว้ได้ แต่สิ่งเหล่านั้นจะไม่เป็นคู่แข่งที่ร้ายแรง
Joe Z.

เหตุใดจึงเป็นคู่แข่งที่ร้ายแรง
theonlygusti

มันไม่ใช่. นั่นเป็นเหตุผลที่ฉันบอกว่าฉันจะประหลาดใจถ้าคู่แข่งร้ายแรงใด ๆ ที่สามารถทำคะแนนได้แย่กว่าคู่แข่งที่ไม่ร้ายแรงนี้
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

สิ่งนี้สามารถแก้ไขได้เสมอ ชิ้นส่วน 3x3 4 ชิ้นจะได้รับการแก้ไขก่อนจากนั้นตามด้วย 8 คอลัมน์จากนั้นตามด้วย 9 แถว


1

PHP - 2,580,210 เบาะแส

นี่เป็นการลบแถวและคอลัมน์สุดท้ายและมุมขวาล่างของทุกช่อง จากนั้นพยายามล้างแต่ละเซลล์เรียกใช้กระดานผ่านตัวแก้ปัญหาอย่างง่ายหลังจากการเปลี่ยนแปลงแต่ละครั้งเพื่อให้แน่ใจว่าบอร์ดยังคงแก้ปัญหาได้อย่างไม่น่าสงสัย

มากของโค้ดด้านล่างนี้ได้รับการดัดแปลงมาจากหนึ่งในคำตอบเก่าของฉัน printBoardใช้ 0s สำหรับเซลล์ว่าง

<?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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.