Вызов Кода: Создайте Программу Для Решения Судоку С Минимальным Набором Подсказок.

  • Автор темы Jarkynbek
  • Обновлено
  • 23, Oct 2024
  • #1

Моя попытка изложить этот вопрос, но с более объективным критерием решения.

Ваша задача — создать программу или функцию, которая использует решенную сетку судоку. S in the format of your choice and attempts to generate a problem grid with as few clues as possible that has S как его уникальное решение. (Неважно, какой метод S is the unique solution by, including brute force, as long as the solution is provably unique.)


Ваша программа будет оценена путем ее запуска через набор из 100 000 сеток решений, найденных в этот файл (загрузка 7,82 МБ) и суммирование количества подсказок во всех 100 000 сетках задач, которые создает ваше решение.

Решения судоку в приведенном выше тестовом файле выражаются в виде строки из 81 символа слева направо, а затем сверху вниз. Код, необходимый для преобразования входных данных тестового файла в пригодное для использования решение, не будет учитываться при подсчете байтов вашего решения.

Как в моем Заливная краска Чтобы решить эту задачу, ваша программа должна фактически выдать действительный результат для всех 100 000 головоломок. Программа, которая выдает наименьшее количество подсказок для всех 100 000 тестовых случаев, становится победителем, а более короткий код разрешает ничью.


Текущее табло:

  1. 2,361,024 - нутки, С
  2. 2,580,210 - es1024, PHP
  3. 6,000,000 - КоверPython, Python 2
  4. 7,200,000 - Джо З., Питон

#вызов кода #оптимизация #судоку #тест-батарея

Jarkynbek


Рег
20 Sep, 2014

Тем
74

Постов
180

Баллов
580
  • 26, Oct 2024
  • #2

C - 2 361 024 2 509 949 подсказок

Удалите подсказки, начиная с последней ячейки, если метод грубой силы находит только одно уникальное решение.

Вторая попытка: используйте эвристику, чтобы решить, в каком порядке удалять подсказки, а не начинать с последней. Из-за этого код работает намного медленнее (20 минут вместо 2 для вычисления результата). Я мог бы сделать решатель быстрее, поэкспериментировать с разными эвристиками, но пока сойдет.

 
 
 
 
 
 
 
 #!/bin/bash

concurrency=$1

killall minimize 2>/dev/null
rm -f ppcg_?? ppcg_??.done
split -n l/$concurrency ppcg_sudoku_testing.txt ppcg_

for p in {0..20}; do

t1=$(date +%s)

lim=$((2**p))

for f in ppcg_??; do

./minimize $lim $f > $f.done &

done

wait

t2=$(date +%s)

cat *.done | awk -F' ' -v t=$((t2 - t1)) -v n=$lim '{ x += $2 } END { printf("%5d: d  %d sec\n",n,x,t); }'
done
 
||answer||

Питон — 7 200 000 подсказок

Как обычно, вот эталонное решение на последнем месте:

#include "include/tdoku.h" #include <cstdio> #include <cstring> #include <fstream> int NumClues(const char *puzzle) { int num_clues = 0; for (int i = 0; i < 81; i++) { if (puzzle[i] != '.') num_clues++; } return num_clues; } int main(int argc, char **argv) { int limit = std::stoi(argv[1]); std::ifstream file; file.open(argv[2]); std::string line; while (getline(file, line)) { char puzzle[81], min_puzzle[81]; int min_clues = 81; for (int j = 0; j < limit; j++) { memcpy(puzzle, line.c_str(), 81); TdokuMinimize(false, puzzle); int clues = NumClues(puzzle); if (clues <= min_clues) { min_clues = clues; memcpy(min_puzzle, puzzle, 81); } } printf("%.81s %d\n", min_puzzle, min_clues); } }

Удаление нижнего ряда чисел, очевидно, оставляет головоломку разрешимой во всех случаях, поскольку в каждом столбце по-прежнему заполнено 8 из 9 чисел, а каждое число в нижнем ряду — это просто девятое число, оставшееся в столбце.

Если какому-либо серьезному сопернику удастся по закону набрать худший результат, чем этот, я буду удивлен.

 

Kelth


Рег
24 Oct, 2010

Тем
73

Постов
175

Баллов
550
  • 26, Oct 2024
  • #3

Python 2 — 6 000 000 подсказок

Простое решение, использующее 3 распространенных метода решения этих головоломок:

# (1) fetch tdoku git clone https://github.com/t-dillon/tdoku.git cd tdoku # (2) pick your compiler (recent clang is best for tdoku): export CC=clang-8 export CXX=clang++-8 # (3) place ppcg_sudoku_testing.txt, minimize.cc (below), # and run.sh (below) in the current directory # (4) build the tdoku solver library ./BUILD.sh # (5) build minimize.cc $CXX -std=c++11 -O3 -march=native -o minimize minimize.cc build/libtdoku.a # (6) run with concurrency appropriate for your system. # results below were run on a 32core/64thread TR-2990WX chmod a+x run.sh ./run.sh 64 1: 2438970 0 sec 2: 2377595 1 sec 4: 2329366 2 sec 8: 2288410 3 sec 16: 2254275 7 sec 32: 2223342 14 sec 64: 2195642 27 sec 128: 2174050 55 sec 256: 2153351 109 sec 512: 2127972 218 sec 1024: 2105589 436 sec 2048: 2091738 871 sec 4096: 2081604 1740 sec

Эта функция создает такие форматы подсказок:

<?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;

Это всегда можно решить. Сначала решаются 4 детали 3х3, затем 8 столбцов, затем 9 рядов.

 

Eugenf


Рег
22 Sep, 2006

Тем
74

Постов
195

Баллов
585
  • 26, Oct 2024
  • #4

PHP — 2 580 210 подсказок

Сначала удаляется последняя строка и столбец, а также нижний правый угол каждого поля. Затем он пытается очистить каждую ячейку, пропуская доску через простой решатель после каждого изменения, чтобы убедиться, что доска по-прежнему однозначно разрешима.

Большая часть приведенного ниже кода была изменена с один из моих старых ответов. printBoard uses 0s for empty cells.

......... .dddddddd .dddddddd .ddd.dd.d .dddddddd .dddddddd .ddd.dd.d .dddddddd .dddddddd ||answer||

C++/Tdoku — 2 081 604 подсказки

Ниже приведены результаты случайной минимизации с использованием тдоку разгадайте каждую головоломку N раз и получите результат с наименьшим количеством подсказок. Результаты показаны для различных N в виде степеней 2. Никакая эвристика вообще не используется. Каждая минимизация просто берет случайную перестановку диапазона (81), а затем пытается удалить подсказки в этом порядке, возвращая подсказку всякий раз, когда ее удаление приводит к множеству решений.

Следует отметить тот факт, что кривая не выравнивается быстро, и меньшие числа явно могут быть достигнуты без каких-либо умений, просто стремясь к большему N.

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

минимизировать.cc:

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

run.sh

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

Изнуренкин


Рег
10 Jan, 2012

Тем
84

Постов
228

Баллов
698
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно