무차별 대항 스도쿠 솔버 구현


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. 솔루션은 합리적인 시간 내에 해결해야합니다.

예제 I / O :

>sudoku.py "030001000006000050500000983080006302000050000903800060714000009020000800000400030"
832591674496387251571264983185746392267953418943812765714638529329175846658429137

입력 길이는 어떻게 27 자입니까? 길이는 81 자-9 행 x 9 열이어야합니다. 그것은 당신의 모범이하는 일입니다. 또한 "누락 된 문자가 0이 될 것"은 문자 수가 81보다 작 으면 0이 끝남을 의미합니까?
Jonathan M Davis

아 잠깐만 누락 된 문자가 0 비트가됩니다. 어. 그것들은 추측해야 할 것들입니다. 어떤 경우에는, 문자의 수는 81이 아니라 27이 될 필요가 않습니다
조나단 M 데이비스

8
규칙 5와 6이 다소 충돌하는 것 같습니다 ....
pseudonym117

답변:


11

k (72 바이트)

이에 대한 크레딧은 k 언어의 창시자 인 Arthur Whitney에게갑니다.

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

권위 있는! 나도 이것을 게시하려고했다!
nightTrevors

9

파이썬, 188 바이트

이것은 CodeSprint Sudoku에 대한 우승작 의 단축 버전 으로 stdin 대신 명령 줄 입력을 위해 수정되었습니다 (OP에 따라).

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를 사용하는 경우 3 바이트를 절약하기 위해 '%d'%5**18바꿀 수 있습니다 `5**18`.

더 빠르게 실행하기 위해 1 바이트의 비용으로 '%d'%5**18순열을 대체 할 수 있습니다 '123456789'.

당신이 그것을 대신하여 stdin의 입력을 수용하려는 경우, 당신은 대체 할 수 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())

편집 : 여기에 자세한 연습에 대한 링크가 있습니다.


아주 좋은 해결책.
primo December

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를 컴파일 할 때 내 페넘 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 (gazillion billion years)


1
그리고 왜 예상 실행 시간이 "O (gazillion billion years)"입니까? (O가 없으면 그것은 단지 "억만 년"이 아닐까요?
Justin Justin

1
내가이 질문을 보았을 때 나는 J가 이것을 분쇄 할 것이라는 것을 즉시 알았습니다. 이것을 K보다 짧게 만드는 방법이 있어야합니다.
koko

1
@Quincunx, 엄밀히 말하면, big-O의 잘못된 사용입니다. "농담"은 "일정한 실행 시간, 무증상 수백만 억 년"이라고 읽었습니다.
Eelvex

@ koko, 나는 더 좋은 것을 찾을 수 없었지만 여전히 노력하고 있습니다.
Eelvex

4

펄, 120 바이트

오, 나는 2008 년에 골프를했던 기억이납니다. 그리고 실제로는 split에 의한 @_의 암시 적 설정이 제거 된 이후로 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
그것은 Clarke의 세 번째 법칙 이지만 반대로!
Conor O'Brien

3

펄, 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 어설 션을 사용하여 행, 열 또는 영역 제약 조건을 위반하는 솔루션을 거부하여 각 행에서 하나의 숫자와 일치하는 정규 표현식을 구성합니다. 그런 다음 문자열을 정규 표현식과 일치시켜 Perl의 정규 표현식 엔진이 시험 및 역 추적의 어려운 작업을 수행하도록합니다.

놀랍게도, 원래 프로그램처럼 모든 입력에 작동하는 단일 정규 표현식을 만들 수 있습니다. 불행히도 속도가 느리므로 골프 코드를 하드 코딩 된 버전 ( FWP 스레드에서 나중에 볼 수 있음)을 기반으로했습니다. )을 기반으로했습니다. 이는 정규 표현식을 조정하여 나중에 제약 조건을 위반하는 솔루션을 조기에 거부합니다. 이것은 특히 어려운 스도쿠를 해결하는 데 다소 오랜 시간이 걸릴 수 있지만, 적당한 수준의 스도쿠를 쉽게 또는 적당히 처리 할 수 ​​있도록합니다.

perl -M5.010Perl 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].
Konrad Borowski

1

클로저-480 바이트

크기는 폭발했지만 적어도 숫자입니다. 1D 벡터 만 사용하면 크게 향상 될 수 있다고 생각합니다. 어쨌든, 테스트 케이스는 랩톱에서 4 초 정도 걸립니다. 결국 함수형 언어이기 때문에 함수를 정의하는 것이 적합하다고 생각했습니다.

(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"

약간 ungolfed (예쁜) 버전 :

(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

펄 (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
직접 작성하지 않은 경우 "커뮤니티 위키"버튼을 확인해야합니다.
Kyle Kanos

그것이 무엇인지 조사한 후에는 불가능합니다. 확인란을 표시하려면 분명히 100 명의 담당자가 필요합니다 ( 이 게시물의 # 2 아래 부록 섹션 참조 )
Qwix

흠, 그 요구 사항을 몰랐습니다.
Kyle Kanos

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.