Thực hiện Bộ giải Sudoku Brute Force


20

Thực hiện trình giải Sudoku ngắn nhất bằng cách đoán. Vì tôi đã nhận được một vài yêu cầu, tôi đã thêm câu hỏi này như một câu hỏi thay thế cho những người muốn thực hiện một người giải sudoku vũ phu.

Câu đố Sudoku:

 | 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

Câu trả lời:

 | 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

Quy tắc:

  1. Giả sử tất cả các mê cung chỉ có thể giải được bằng logic.
  2. Tất cả đầu vào sẽ dài 81 ký tự. Thiếu ký tự sẽ là 0.
  3. Xuất ra giải pháp dưới dạng một chuỗi đơn.
  4. "Lưới" có thể được lưu trữ nội bộ theo cách bạn muốn.
  5. Các giải pháp phải sử dụng một giải pháp đoán lực lượng vũ phu.
  6. Giải pháp nên giải quyết trong một giới hạn thời gian hợp lý.

Ví dụ I / O:

>sudoku.py "030001000006000050500000983080006302000050000903800060714000009020000800000400030"
832591674496387251571264983185746392267953418943812765714638529329175846658429137

Làm thế nào đầu vào có thể dài 27 ký tự? Nó cần dài 81 ký tự - 9 hàng x 9 cột. Đó là những gì ví dụ của bạn làm quá. Ngoài ra, tôi giả sử rằng "các ký tự bị thiếu sẽ là 0" có nghĩa là nếu số lượng ký tự ít hơn 81, thì các số 0 có kết thúc không?
Jonathan M Davis

Oh, đợi đã. Tôi nhận được các ký tự bị thiếu sẽ là 0 bit. Duh. Đó là những người cần phải đoán. Trong mọi trường hợp, số lượng nhân vật không cần phải là 81, không phải 27.
Jonathan M Davis

8
có vẻ như các quy tắc 5 và 6 xung đột ....
bút danh

Câu trả lời:


11

k (72 byte)

Tín dụng cho điều này thuộc về Arthur Whitney, người tạo ra ngôn ngữ k.

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

cổ điển! Tôi cũng sẽ đăng bài này!
nightTrevors

9

Python, 188 byte

Đây là phiên bản rút gọn hơn nữa trong bài nộp chiến thắng của tôi cho CodeSprint Sudoku , được sửa đổi cho đầu vào dòng lệnh thay vì stdin (theo 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])

Nếu bạn đang sử dụng Python 2, '%d'%5**18có thể được thay thế bằng `5**18`để lưu 3 byte.

Để làm cho nó chạy nhanh hơn, bạn có thể thay thế '%d'%5**18bằng bất kỳ hoán vị nào với '123456789'chi phí 1 byte.

Thay vào đó, nếu bạn muốn nó chấp nhận đầu vào trên stdin, bạn có thể thay thế import sys;f(sys.argv[1])bằng f(raw_input()), đưa nó xuống 177 byte .

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: Đây là một liên kết đến một hướng dẫn chi tiết hơn.


Giải pháp rất hay.
primo

8

Con trăn, 197 ký tự

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

Trả lời trong 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;
}

Với đầu vào mẫu, phải mất 0,33 giây trên Phenom II X6 1090T của tôi khi được biên dịch với dmd -w(tức là không tối ưu hóa) và phải mất 0,011 giây khi được biên dịch dmd -w -O -inline -release(tức là với tối ưu hóa).


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

thời gian chạy dự kiến: O (gazillion tỷ năm)


1
Và tại sao thời gian chạy dự kiến ​​"O (gazillion tỷ năm)"? (không nên chỉ là "triệu tỷ năm" mà không có chữ O?
Justin

1
Khi tôi thấy câu hỏi này, tôi lập tức biết rằng J sẽ nghiền nát câu hỏi này. Phải có một cách để làm cho nó ngắn hơn K.
koko

1
@Quincunx, nói đúng ra, đó là việc sử dụng big-O sai; "trò đùa" được cho là đọc "thời gian chạy liên tục, không có triệu chứng triệu năm".
Eelvex

@koko, tôi không thể tìm thấy cái gì tốt hơn nhưng tôi vẫn đang làm việc với nó.
Eelvex

4

Perl, 120 byte

Ồ, tôi nhớ rằng chơi golf hồi năm 2008 ... Và thực tế nó đã ngừng hoạt động trong perl 5.12 vì cài đặt ngầm định của @_ bằng cách chia đã bị xóa sau đó. Vì vậy, chỉ thử điều này trên một perl đủ cũ.

Chạy với đầu vào trên 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
Đó là luật thứ ba của Clarke , nhưng ngược lại!
Conor O'Brien

3

Perl, 235 ký tự

$_=$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/

Đây là phiên bản được đánh gôn của một cái gì đó tôi đã đăng nhiều năm trước vào danh sách gửi thư của Fun With Perl : một regrec giải quyết sudoku.

Về cơ bản, nó xâu chuỗi đầu vào thành 81 dòng, mỗi dòng chứa tất cả các số có thể xảy ra trong ô vuông tương ứng. Sau đó, nó xây dựng một biểu thức chính quy để khớp một số từ mỗi dòng, sử dụng các phản hồi ngược và các xác nhận nhìn tiêu cực để từ chối các giải pháp vi phạm các ràng buộc của hàng, cột hoặc vùng. Sau đó, nó khớp chuỗi với regrec, để cho công cụ regrec của Perl thực hiện công việc thử nghiệm và quay lui khó khăn.

Đáng kinh ngạc, có thể tạo một biểu thức chính quy hoạt động cho mọi đầu vào, giống như chương trình ban đầu của tôi. Thật không may, nó khá chậm, vì vậy tôi đã dựa vào mã đánh gôn ở đây trên phiên bản mã hóa cứng ( được tìm thấy sau trong chuỗi FWP ), điều chỉnh regrec để từ chối sớm mọi giải pháp mà nó biết sau đó sẽ vi phạm một ràng buộc. Điều này làm cho nó nhanh chóng hợp lý cho sudokus cấp độ dễ đến trung bình, mặc dù những người đặc biệt khó khăn vẫn có thể mất một thời gian khá dài để giải quyết.

Chạy mã với perl -M5.010để kích hoạt saytính năng Perl 5.10+ . Đầu vào phải được đưa ra trên đầu vào tiêu chuẩn, và giải pháp sẽ được in thành đầu ra tiêu chuẩn; thí dụ:

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

2

Cà phê 1 lớp lót

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

Đây là phiên bản lớn hơn với cách sử dụng mẫu :

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
Có thể rút ngắn bằng cách rút ngắn solve, loại bỏ nhiều khoảng trắng (tôi biết nó có ý nghĩa, nhưng ở nhiều nơi có thể xóa), sử dụng các ký hiệu thay vì từ (như !=thay vì isnt), sử dụng thụt lề thay vì thentừ khóa, thay thế [0...9]bằng [0..8].
Konrad Borowski

1

Clojure - 480 byte

Kích thước phát nổ, nhưng ít nhất nó là một con số đẹp. Tôi nghĩ rằng nó có thể được cải thiện rất nhiều bằng cách chỉ sử dụng vector 1D. Dù sao, trường hợp thử nghiệm mất ít hơn bốn giây trên máy tính xách tay của tôi. Tôi nghĩ rằng nó sẽ phù hợp để xác định một chức năng, vì cuối cùng nó là một ngôn ngữ chức năng.

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

Ví dụ:

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

Một phiên bản hơi vô văn hóa (và đẹp hơn):

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

$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}}

Hãy thử trực tuyến!

Kịch bản tìm thấy tất cả các giải pháp cho một sudoku.

Chưa được kiểm soát:

$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
    }

}

Các trường hợp thử nghiệm:

@(
    # 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 ký tự)

Đối với mỗi ô vuông chưa giải quyết, nó xây dựng một loạt các tùy chọn có sẵn và sau đó lặp lại trên nó.

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

với khoảng trắng:

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 ký tự)

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

Tất cả tín dụng thuộc về người sáng tạo ở đây , và lời giải thích cũng có thể được tìm thấy ở đó.


1
Nếu bạn không tự viết, thì bạn nên kiểm tra nút "Wiki cộng đồng".
Kyle Kanos

Sau một số nghiên cứu về những gì nó là, nó không thể đối với tôi. Rõ ràng cần 100 rep để tôi xem hộp kiểm (xem phần phụ lục trong mục số 2 của bài đăng này )
Qwix

Hmm, không nhận thức được yêu cầu đó.
Kyle Kanos

0

J, 94 byte

Hoạt động chính xác giống như phiên bản K, cụ thể là với BFS (vì vậy nó sẽ xuất ra tất cả các giải pháp). Nó in khoảng trắng giữa các chữ số đầu ra, nhưng chương trình K cũng vậy. Tôi không đếm được s =: vì đây chỉ là đặt tên cho hàm (giống như tôi sẽ không đếm tên tệp bằng ngôn ngữ khác).

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