Впровадьте жорстоку розв'язку судоку


20

Реалізуйте найкоротший розв'язувач судоку за допомогою здогадок. Оскільки я отримав декілька запитів, я додав це як альтернативне запитання для тих, хто бажає застосувати жорстоку розв'язку судоку.

Головоломка судоку:

 | 1 2 3 | 4 5 6 | 7 8 9
-+-----------------------
A|   3   |     1 |
B|     6 |       |   5
C| 5     |       | 9 8 3
-+-----------------------
D|   8   |     6 | 3   2
E|       |   5   |
F| 9   3 | 8     |   6
-+-----------------------
G| 7 1 4 |       |     9
H|   2   |       | 8
I|       | 4     |   3

Відповідь:

 | 1 2 3 | 4 5 6 | 7 8 9
-+-----------------------
A| 8 3 2 | 5 9 1 | 6 7 4
B| 4 9 6 | 3 8 7 | 2 5 1
C| 5 7 1 | 2 6 4 | 9 8 3
-+-----------------------
D| 1 8 5 | 7 4 6 | 3 9 2
E| 2 6 7 | 9 5 3 | 4 1 8
F| 9 4 3 | 8 1 2 | 7 6 5
-+-----------------------
G| 7 1 4 | 6 3 8 | 5 2 9
H| 3 2 9 | 1 7 5 | 8 4 6
I| 6 5 8 | 4 2 9 | 1 3 7

Правила:

  1. Припустимо, що всі лабіринти вирішуються лише логікою.
  2. Увесь вхід матиме 81 символ. Зниклих символів буде 0.
  3. Виведіть рішення у вигляді одного рядка.
  4. "Сітка" може зберігатися всередині, як тільки ви хочете.
  5. Розв’язання повинно застосовувати грубу силу вгадування.
  6. Рішення повинні вирішуватися в розумний термін.

Приклад вводу / виводу:

>sudoku.py "030001000006000050500000983080006302000050000903800060714000009020000800000400030"
832591674496387251571264983185746392267953418943812765714638529329175846658429137

Як може вводитися 27 символів? Потрібно мати 81 символ - 9 рядків x 9 стовпців. Ось що робить і ваш приклад. Також я припускаю, що "відсутніх символів буде 0" означає, що якщо кількість символів менше 81, то нулі йдуть на кінець?
— Джонатан М Девіс

Зачекайте. Я отримую відсутніх символів буде 0 біт. Дух. Це ті, про які потрібно здогадатися. У будь-якому випадку кількість символів має бути 81, а не 27.
— Джонатан М Девіс

8
здається, правила 5 і 6 свого роду конфлікт ....
— псевдонім117

Відповіді:


11

k (72 байти)

За це заслуговує Артур Вітні, творець мови k.

p,:3/:_(p:9\:!81)%3
s:{*(,x)(,/{@[x;y;:;]'&21=x[&|/p[;y]=p]?!10}')/&~x}

класичний! Я збирався це також відправити!
— nightTrevors

9

Пітон, 188 байт

Це ще одна скорочена версія мого виграшного подання для CodeSprint Sudoku , модифікованого для введення командного рядка замість stdin (відповідно до ОП):

def f(s):
 x=s.find('0')
 if x<0:print s;exit()
 [c in[(x-y)%9*(x/9^y/9)*(x/27^y/27|x%9/3^y%9/3)or s[y]for y in range(81)]or f(s[:x]+c+s[x+1:])for c in'%d'%5**18]
import sys
f(sys.argv[1])

Якщо ви використовуєте Python 2, '%d'%5**18його можна замінити, `5**18`щоб зберегти 3 байти.

Щоб зробити його швидшим, ви можете замінити '%d'%5**18будь-яку перестановку '123456789'вартістю 1 байт.

Якщо ви хочете , щоб прийняти введення на стандартний ввід замість цього, ви можете замінити import sys;f(sys.argv[1])з f(raw_input()), довівши його до 177 байт .

def f(s):
 x=s.find('0')
 if x<0:print s;exit()
 [c in[(x-y)%9*(x/9^y/9)*(x/27^y/27|x%9/3^y%9/3)or s[y]for y in range(81)]or f(s[:x]+c+s[x+1:])for c in'%d'%5**18]
f(raw_input())

EDIT: Ось посилання на більш детальну інформацію.


Дуже приємне рішення.
— примо

8

Пітон, 197 символів

def S(s):
 i=s.find('0')
 if i<0:print s;return
 for v in'123456789':
  if sum(v==s[j]and(i/9==j/9or i%9==j%9or(i%9/3==j%9/3and i/27==j/27))for j in range(81))==0:S(s[:i]+v+s[i+1:])
S(raw_input())

6

Відповідь у D:

import std.algorithm;
import std.conv;
import std.ascii;
import std.exception;
import std.stdio;

void main(string[] args)
{
    enforce(args.length == 2, new Exception("Missing argument."));
    enforce(args[1].length == 81, new Exception("Invalid argument."));
    enforce(!canFind!((a){return !isDigit(to!dchar(a));})
                     (args[1]),
                      new Exception("Entire argument must be digits."));

    auto sudoku = new Sudoku(args[1]);
    sudoku.fillIn();

    writeln(sudoku);
}

class Sudoku
{
public:

    this(string str) nothrow
    {
        normal = new int[][](9, 9);

        for(size_t i = 0, k =0; i < 9; ++i)
        {
            for(size_t j = 0; j < 9; ++j)
                normal[i][j] = to!int(str[k++]) - '0';
        }

        reversed = new int*[][](9, 9);

        for(size_t i = 0; i < 9; ++i)
        {
            for(size_t j = 0; j < 9; ++j)
                reversed[j][i] = &normal[i][j];
        }

        boxes = new int*[][](9, 9);
        indexedBoxes = new int*[][][](9, 9);

        for(size_t boxRow = 0, boxNum = 0; boxRow < 3; ++boxRow)
        {
            for(size_t boxCol = 0; boxCol < 3; ++boxCol, ++boxNum)
            {
                for(size_t i = 3 * boxRow, square = 0; i < 3 * (boxRow + 1); ++i)
                {
                    for(size_t j = 3 * boxCol; j < 3 * (boxCol + 1); ++j)
                    {
                        boxes[boxNum][square++] = &normal[i][j];
                        indexedBoxes[i][j] = boxes[boxNum];
                    }
                }
            }
        }
    }

    void fillIn()
    {
        fillIn(0, 0);
    }

    @property bool valid()
    {
        assert(full);

        for(size_t i = 0; i < 9; ++i)
        {
            for(int n = 1; n < 10; ++n)
            {
                if(!canFind(normal[i], n) ||
                   !canFind!"*a == b"(reversed[i], n) ||
                   !canFind!"*a == b"(boxes[i], n))
                {
                    return false;
                }
            }
        }

        return true;
    }

    override string toString() const
    {
        char[81] retval;

        for(size_t i = 0, k =0; i < 9; ++i)
        {
            for(size_t j = 0; j < 9; ++j)
                retval[k++] = to!char(normal[i][j] + '0');
        }

        return to!string(retval);
    }

private:

    @property bool full()
    {
        for(size_t i = 0; i < 9; ++i)
        {
            if(canFind(normal[i], 0))
                return false;
        }

        return true;
    }

    bool fillIn(size_t row, size_t col)
    {
        if(row == 9)
            return valid;

        size_t nextRow = row;
        size_t nextCol = col + 1;

        if(nextCol == 9)
        {
            nextRow = row + 1;
            nextCol = 0;
        }

        if(normal[row][col] == 0)
        {
            for(int n = 1; n < 10; ++n)
            {
                if(canFind(normal[row], n) ||
                   canFind!"*a == b"(reversed[col], n) ||
                   canFind!"*a == b"(indexedBoxes[row][col], n))
                {
                    continue;
                }

                normal[row][col] = n;

                if(fillIn(nextRow, nextCol))
                    return true;
            }

            normal[row][col] = 0;

            return false;
        }
        else
            return fillIn(nextRow, nextCol);
    }

    int[][] normal;
    int*[][] reversed;
    int*[][] boxes;
    int*[][][] indexedBoxes;
}

З зразком введення, він приймає .033s на мій Phenom II X6 1090T при компіляції з dmd -w(тобто без оптимізації), і він приймає .011s при компіляції з dmd -w -O -inline -release(тобто з оптимізацією).


4

J, 103

'p n'=:(;#)I.0=a=:("."0)Y
((a p}~3 :'>:?n#9')^:([:(27~:[:+/[:(9=#@~.)"1[:,/(2 2$3),;.3],|:,])9 9$])^:_)a

очікуваний час виконання: O (мільярд мільярдів років)


1
І чому очікуваний час роботи "O (Газліон мільярдів років)"? (чи не повинно це бути просто "мільярд мільярдів років" без О?
— Джастін

1
Побачивши це запитання, я негайно зрозумів, що J збирається розчавити це питання. Має бути спосіб зробити це коротшим, ніж К.
— Коко

1
@Quincunx, строго кажучи, це неправильне використання big-O; "жарт" повинен був читати "постійний час пробігу, асимптотично, мільярд мільярдів років".
— Енайсекс

@koko, я не зміг знайти щось краще, але я все ще працюю над цим.
— Вісімнадцять

4

Perl, 120 байт

О, я пам’ятаю гольф, що ще в 2008 році ... І він фактично перестав працювати в perl 5.12, оскільки неявна установка @_ розділом була видалена. Тож спробуйте це лише на досить старому перлі.

Запустити з введенням STDIN:

sudoku.pl <<< "030001000006000050500000983080006302000050000903800060714000009020000800000400030"

sudoku.pl:

${/[@_[map{$i-($i="@-")%9+$_,9*$_+$i%9,9*$_%26+$i-$i%3+$i%9-$i%27}0..8%split""]]/o||do$0}for$_=$`.$_.$'.<>,/0/||print..9

2
Це третій закон Кларка , але навпаки!
— Conor O'Brien

3

Perl, 235 символів

$_=$s=<>;$r=join$/,map{$n=$_;'.*(?!'.(join'|',map+($_%9==$n%9||int($_/9)==int($n/9)||int($_/27)==int($n/27)&&int($_/3%3)==int($n/3%3)and$_<$n?'\\'.($_+1):$_>$n&&substr$s,$_,1)||X,@a).')(.).*'}@a=0..80;s!.!($&||123456789).$/!eg;say/^$r/

Це гольф-версія чогось, що я розмістив багато років тому у списку розсилки Fun With Perl : підсумок для вирішення судоку.

В основному, він керує входом у 81 рядок, кожен з яких містить усі числа, які можуть бути у відповідному квадраті. Потім він створює регулярне вираження для відповідності одному номеру з кожного рядка, використовуючи зворотні посилання та негативні твердження lookahead для відхилення рішень, що порушують обмеження рядків, стовпців або регіонів. Тоді вона співпадає з рядком проти regexp, дозволяючи двигуну regexp Perl виконувати важку роботу пробного і зворотного відстеження.

Дивно, але можливо створити єдиний регулярний параметр, який працює для будь-якого вводу, як це робить моя оригінальна програма. На жаль, це досить повільно, тому я базував тут код для гольфу на версії з твердим кодом (дана пізніше в потоці FWP ), яка налаштовує регулярний вираз, щоб рано відхиляти будь-які рішення, які він знає, згодом порушить обмеження. Це робить досить швидким для легкого до помірного рівня судокусів, хоча особливо важкі можуть все-таки зайняти досить тривалий час.

Запустіть код, perl -M5.010щоб увімкнути функцію Perl 5.10+ say. Вхід повинен бути вказаний на стандартному вході, і рішення буде надруковано на стандартний вихід; приклад:

$ perl -M5.010 golf/sudoku.pl
030001000006000050500000983080006302000050000903800060714000009020000800000400030
832591674496387251571264983185746392267953418943812765714638529329175846658429137

2

1-лайнерна кава-сценарій

solve = (s, c = 0) -> if c is 81 then s else if s[x = c/9|0][y = c%9] isnt 0 then solve s, c+1 else (([1..9].filter (g) -> ![0...9].some (i) -> g in [s[x][i], s[i][y], s[3*(x/3|0) + i/3|0][3*(y/3|0) + i%3]]).some (g) -> s[x][y] = g; solve s, c+1) or s[x][y] = 0

Ось більша версія із використанням зразка :

solve = (sudoku, cell = 0) ->
  if cell is 9*9 then return sudoku

  x = cell%9
  y = (cell - x)/9

  if sudoku[x][y] isnt 0 then return solve sudoku, cell+1

  row = (i) -> sudoku[x][i]
  col = (i) -> sudoku[i][y]
  box = (i) -> sudoku[x - x%3 + (i - i%3)/3][y - y%3 + i%3]

  good = (guess) -> [0...9].every (i) -> guess not in [row(i), col(i), box(i)]

  guesses = [1..9].filter good

  solves = (guess) -> sudoku[x][y] = guess; solve sudoku, cell+1

  (guesses.some solves) or sudoku[x][y] = 0

sudoku = [
  [1,0,0,0,0,7,0,9,0],
  [0,3,0,0,2,0,0,0,8],
  [0,0,9,6,0,0,5,0,0],
  [0,0,5,3,0,0,9,0,0],
  [0,1,0,0,8,0,0,0,2],
  [6,0,0,0,0,4,0,0,0],
  [3,0,0,0,0,0,0,1,0],
  [0,4,0,0,0,0,0,0,7],
  [0,0,7,0,0,0,3,0,0]
]
console.log if solve sudoku then sudoku else 'could not solve'

1
Можна скоротити, скорочуючи solve, видаляючи багато пробілів (я знаю, що це важливо, але в багатьох місцях його можна видалити), використовуючи символи замість слів (як-от !=замість isnt), використовуючи відступ замість thenключового слова, замінюючи [0...9]на [0..8].
— Конрад Боровський

1

Clojure - 480 байт

Розмір вибухнув, але принаймні це досить велика кількість. Я думаю, що це можна було б значно покращити, використовуючи лише 1D-вектор. У будь-якому випадку на моєму ноутбуці тестовий випадок займає трохи менше чотирьох секунд. Я думав, що було б визначити функцію, оскільки це функціональна мова.

(defn f[o &[x y]](if x(if(> y 8)(apply str(map #(apply str %)o))(first(for[q[(o y)]v(if(=(q x)0)(range 1 10)[(q x)])d[(assoc o y(assoc(o y)x v))]s[(and(every? true?(concat(for[i(range 9)](and(or(not=((d y)i)v)(= i x))(or(not=((d i)x)v)(= i y))))(for[m[#(+ %2(- %(mod % 3)))]r[(range 3)]a r b r c[(m y b)]e[(m x a)]](or(and(= e x)(= c y))(not=((d y)x)((d c)e))))))(f d(mod(+ x 1)9)(if(= x 8)(+ 1 y)y)))]:when s]s)))(f(vec(for[a(partition 9 o)](vec(map #(Integer.(str %))a))))0 0)))

Приклади:

(f "030001000006000050500000983080006302000050000903800060714000009020000800000400030")
=> "832591674496387251571264983185746392267953418943812765714638529329175846658429137"
(f "004720900039008005001506004040010520028050170016030090400901300100300840007085600")
=> "654723981239148765871596234743819526928654173516237498482961357165372849397485612"

Трохи невольф (і гарніша) версія:

(defn check-place [o x y v]
  (and (every? true? (for [i (range 9)]
                       (and (or (not= ((o y) i) v) (= i x))
                            (or (not= ((o i) x) v) (= i y)))))
       (every? true?
         (for [r [(range 3)]
               a r
               b r
               c [(+ b (- y (mod y 3)))]
               d [(+ a (- x (mod x 3)))]]
           (or (and (= d x) (= c y)) (not= ((o y) x) ((o c) d)))))))

(defn solve-sudoku [board & [x y]]
  (if x
    (if (> y 8)
      (apply str (map #(apply str %) board))
      (first
        (for [v (if (= ((board y) x) 0) (range 1 10) [((board y) x)])
              :let [a (mod (+ x 1) 9)
                    b (if (= x 8) (+ 1 y) y)
                    d (assoc board y (assoc (board y) x v))
                    s (and (check-place d x y v) (solve-sudoku d a b))]
              :when s]
          s)))
    (solve-sudoku (vec (for [a (partition 9 board)]
                         (vec (map #(Integer. (str %)) a)))) 0 0)))

1

PowerShell , 244 242 218 215 байт

$a=(24,24,6)*3|%{,(0..8|%{($r++)});,(0..8|%{$c%81;$c+=9});$c++;,((1,1,7)*3|%{+$q;$q+=$_});$q-=$_}
$f={param($s)$l,$r=$s-split0,2;if($p=$a|?{$l.length-in$_}){1..9|?{"$_"-notin($p|%{$s[$_]})}|%{&$f "$l$_$r"}}else{$s}}

Спробуйте в Інтернеті!

Сценарій знаходить усі рішення судоку.

Розгорнуто:

$a=(24,24,6)*3|%{                       # array of indexes for a sudoku...
    ,(0..8|%{($r++)})                   # rows
    ,(0..8|%{$c%81;$c+=9});$c++         # columns
    ,((1,1,7)*3|%{+$q;$q+=$_});$q-=$_   # and squares
}

$f = {
    param($s)

    # optional log. remove this statement in a release version.
    if($script:iter++ -lt 100 -or ($script:iter%100)-eq0){
        Write-Information ('{0}: {1,6}: {2}'-f (get-Date), $script:iter, ($s-replace0,' ')) -InformationAction Continue
    }

    $left,$right=$s-split0,2                # split by a first 0; $left.length is a position of this 0 if $s contains the 0
    if( $parts=$a|?{$left.length-in$_} ){   # get sudoku parts (rows, columns, squares) contain the position
        1..9|?{                             # try a digit
            "$_"-notin($parts|%{$s[$_]})    # all digits in these parts will be unique if parts do not contain the digit
        }|%{
            &$f "$left$_$right"             # recursive call with the digit
        } #|select -f 1                     # uncomment this to get a first result only
    }
    else{
        $s
    }

}

Тестові приклади:

@(
    # 5 iterations, my notebook: 00:00:00, all
    # 5 iterations, my notebook: 00:00:00, first only
    , ( "832591674496387251571264983185746392267953418943812765714638529329175846658400030",
        "832591674496387251571264983185746392267953418943812765714638529329175846658429137" )

    # ~29600 iterations, my notebook: 00:01:27, all
    #  ~2100 iterations, my notebook: 00:00:10, first only
    # , ( "830001000006000050500000983080006302000050000903800060714000009020000800000400030",
    #     "832591674496387251571264983185746392267953418943812765714638529329175846658429137" )

    # ~49900 iterations, my notebook: 00:02:39, all
    # ~22400 iterations, my notebook: 00:01:20, first only
    # , ( "030001000006000050500000983080006302000050000903800060714000009020000800000400030",
    #     "832591674496387251571264983185746392267953418943812765714638529329175846658429137" )

) | % {
    $sudoku, $expected = $_
    $time = Measure-Command {
        $result = &$f $sudoku
    }
    "$($result-contains$expected): $time"
    $result
}

0

D (322 символів)

Для кожного невирішеного квадрата він створює масив доступних опцій, а потім петлює над ним.

import std.algorithm,std.range,std.stdio;void main(char[][]args){T s(T)(T p){foreach(i,ref c;p)if(c<49){foreach(o;"123456789".setDifference(chain(p[i/9*9..i/9*9+9],p[i%9..$].stride(9),p[i/27*27+i%9/3*3..$][0..21].chunks(3).stride(3).joiner).array.sort)){c=o&63;if(s(p))return p;}c=48;return[];}return p;}s(args[1]).write;}

з пробілом:

import std.algorithm, std.range, std.stdio;

void main(char[][] args) {
    T s(T)(T p) {
        foreach (i, ref c; p) if (c < 49) {
            foreach (o; "123456789".setDifference(chain(
                    p[i/9*9..i/9*9+9],
                    p[i%9..$].stride(9),
                    p[i/27*27+i%9/3*3..$][0..21].chunks(3).stride(3).joiner
                ).array.sort))
            {
                c = o&63;
                if (s(p)) return p;
            }
            c=48;
            return [];
        }
        return p;
    }
    s(args[1]).write;
}

0

Perl (195 годин)

use integer;@A=split//,<>;sub R{for$i(0..80){next if$A[$i];my%t=map{$_/9==$/9||$_%9==$i%9||$_/27==$i/27&&$_%9/3==$i%9/3?$A[$_]:0=>1}0..80;R($A[$i]=$_)for grep{!$t{$_}}1..9;return$A[$i]=0}die@A}R

Всі заслуга творця тут , і пояснення можна знайти там.


1
Якщо ви самі цього не написали, то слід встановити кнопку "Community Wiki".
— Кайл Канос

Після деяких досліджень, що це таке, для мене це не здається можливим. Мабуть, мені потрібно 100 представників, щоб побачити прапорець (див. Розділ з додатком під №2 цього допису )
— Qwix

Хм, не знав про цю вимогу.
— Кайл Канос

0

J, 94 байти

Працює точно так само, як і версія K, а саме з BFS (таким чином, він повинен виводити всі рішення). Він друкує пробіли між вихідними цифрами, але це робить і програма K. Я не рахую "s =:", оскільки це лише називання функції (так само, як я б не вважав ім'я файлу іншою мовою).

   s=: [:<@((]i.0:)}"0 _~(>:i.9)-.{&((+./ .=|:)3(],.[,@#.<.@%~)9 9#:i.81)@i.&0#])"1^:(0 e.,)@;^:_"."0

   s'030001000006000050500000983080006302000050000903800060714000009020000800000400030'
8 3 2 5 9 1 6 7 4 4 9 6 3 8 7 2 5 1 5 7 1 2 6 4 9 8 3 1 8 5 7 4 6 3 9 2 2 6 7 9 5 3 4 1 8 9 4 3 8 1 2 7 6 5 7 1 4 6 3 8 5 2 9 3 2 9 1 7 5 8 4 6 6 5 8 4 2 9 1 3 7
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.