Implementieren Sie einen Brute Force Sudoku Solver

20

Implementieren Sie den kürzesten Sudoku-Löser mit Raten. Da ich einige Anfragen erhalten habe, habe ich diese als alternative Frage für diejenigen hinzugefügt , die einen Brute-Force-Sudoku-Löser implementieren möchten.

Sudoku-Puzzle:

 | 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

Antworten:

 | 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

Regeln:

  1. Angenommen, alle Labyrinthe sind nur durch Logik lösbar.
  2. Alle Eingaben sind 81 Zeichen lang. Fehlende Zeichen sind 0.
  3. Die Lösung als einzelne Zeichenfolge ausgeben.
  4. Das "Gitter" kann nach Belieben intern gespeichert werden.
  5. Die Lösung muss eine Brute-Force-Rate-Lösung verwenden.
  6. Lösungen sollten innerhalb einer angemessenen Frist gelöst werden.

Beispiel I / O:

>sudoku.py "030001000006000050500000983080006302000050000903800060714000009020000800000400030"
832591674496387251571264983185746392267953418943812765714638529329175846658429137
snmcdonald
quelle
Wie kann die Eingabe 27 Zeichen lang sein? Es muss 81 Zeichen lang sein - 9 Zeilen x 9 Spalten. Das macht auch Ihr Beispiel. Außerdem gehe ich davon aus, dass "fehlende Zeichen werden 0" bedeutet, dass, wenn die Anzahl der Zeichen kleiner als 81 ist, die Nullen am Ende gehen?
Jonathan M Davis
Oh, Moment mal. Ich bekomme die fehlenden Zeichen 0 Bit. Duh. Das sind diejenigen, die erraten werden müssen. In jedem Fall muss die Anzahl der Zeichen 81 sein, nicht 27.
Jonathan M Davis
8
Es scheint, Regeln 5 und 6 irgendwie Konflikt ....
Pseudonym117

Antworten:

11

k (72 Bytes)

Das Verdienst geht an Arthur Whitney, den Schöpfer der k-Sprache.

p,:3/:_(p:9\:!81)%3
s:{*(,x)(,/{@[x;y;:;]'&21=x[&|/p[;y]=p]?!10}')/&~x}
Skeevey
quelle
klassisch! Ich wollte das auch posten!
NightTrevors
9

Python, 188 Bytes

Dies ist eine weitere gekürzte Version meines Siegerbeitrags für CodeSprint Sudoku , der für die Befehlszeileneingabe anstelle von stdin (gemäß OP) geändert wurde:

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

Wenn Sie Python 2 verwenden, '%d'%5**18kann durch ersetzt werden`5**18` , um 3 Bytes zu sparen.

Damit es schneller läuft, können Sie es durch '%d'%5**18eine Permutation '123456789'von 1 Byte ersetzen .

Wenn Sie den Eingang auf der Standardeingabe anstatt anzunehmen möchten, können Sie ersetzen import sys;f(sys.argv[1])mit f(raw_input()), es bringt nach unten 177 Bytes .

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

BEARBEITEN: Hier ist ein Link zu einer detaillierteren Anleitung.

Cyphase
quelle
Sehr schöne Lösung.
Primo
8

Python, 197 Zeichen

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())
Keith Randall
quelle
6

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

Mit der Beispieleingabe dauert es auf meinem Phenom II X6 1090T 0,033 Sekunden, wenn es mit kompiliert wirddmd -w (dh ohne Optimierungen) kompiliert wird , und 0,011 Sekunden, wenn es mit dmd -w -O -inline -release(dh mit Optimierungen) kompiliert wird .

Jonathan M Davis
quelle
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

erwartete Laufzeit: O (Milliarden Jahre)

Eelvex
quelle
1
Und warum ist die erwartete Laufzeit "O (Milliarden Jahre)"? (Sollte das nicht nur "Milliarden Jahre" ohne das O sein?
Justin
1
Als ich diese Frage sah, wusste ich sofort, dass J diese vernichten wird. Es muss einen Weg geben, dies kürzer als K.
koko
1
@Quincunx, streng genommen ist es eine falsche Verwendung von Big-O; der "witz" sollte lauten "konstante laufzeit, asymptotisch milliarden jahre".
Eelvex
@koko, ich konnte nichts besseres finden, arbeite aber noch daran.
Eelvex
4

Perl, 120 Bytes

Oh, ich erinnere mich, dass ich das 2008 beim Golfen gemacht habe ... Und es hat tatsächlich aufgehört, in Perl 5.12 zu funktionieren, da die implizite Einstellung von @_ by split dann entfernt wurde. Versuchen Sie dies also nur auf einem ausreichend alten Perl.

Führen Sie mit der Eingabe auf STDIN aus:

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
Tonne Hospel
quelle
2
Es ist Clarkes drittes Gesetz , aber umgekehrt!
Conor O'Brien
3

Perl, 235 Zeichen

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

Dies ist eine Golf-Version von etwas, das ich vor vielen Jahren in die Fun With Perl-Mailingliste aufgenommen habe : eine Regexp zur Sudoku-Lösung.

Grundsätzlich werden die Eingaben in 81 Zeilen aufgeteilt, von denen jede alle Zahlen enthält, die im entsprechenden Quadrat vorkommen können. Anschließend wird ein regulärer Ausdruck erstellt, der mit einer Zahl aus jeder Zeile übereinstimmt. Dabei werden Rückverweise und negative Lookahead-Aussagen verwendet, um Lösungen abzulehnen, die die Einschränkungen für Zeilen, Spalten oder Bereiche verletzen. Dann wird die Zeichenfolge mit dem regulären Ausdruck abgeglichen, sodass die reguläre Ausdrucks-Engine von Perl die harte Arbeit des Testens und Zurückverfolgens erledigt.

Erstaunlicherweise ist es möglich, einen einzelnen regulären Ausdruck zu erstellen, der für jede Eingabe funktioniert, wie es mein ursprüngliches Programm tut. Leider ist es ziemlich langsam, daher habe ich den Code hier auf der Grundlage der hartcodierten Version von Givens ( die später im FWP-Thread zu finden ist ) erstellt, mit der der reguläre Ausdruck optimiert wird, um Lösungen, von denen bekannt ist, dass sie später eine Einschränkung verletzen, frühzeitig abzulehnen. Dies macht es relativ schnell für einfache bis mittelschwere Sudokus, obwohl die Lösung besonders schwerer Sudokus noch einige Zeit in Anspruch nehmen kann.

Führen Sie den Code mit aus perl -M5.010, um die Perl 5.10+ say-Funktion zu aktivieren . Die Eingabe sollte auf der Standardeingabe erfolgen, und die Lösung wird auf der Standardausgabe gedruckt. Beispiel:

$ perl -M5.010 golf/sudoku.pl
030001000006000050500000983080006302000050000903800060714000009020000800000400030
832591674496387251571264983185746392267953418943812765714638529329175846658429137
Ilmari Karonen
quelle
2

1-Liner-Kaffee-Skript

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

Hier ist die größere Version mit Beispielnutzung :

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'
Pathikrit
quelle
1
Könnte durch Verkürzen solve, Entfernen vieler Leerzeichen (ich weiß, es ist bedeutend, aber an vielen Stellen könnte es entfernt werden), Verwenden von Symbolen anstelle von Wörtern (wie !=anstelle von isnt), Verwenden von Einrückungen anstelle von thenSchlüsselwörtern, Ersetzen [0...9]durch gekürzt werden [0..8].
Konrad Borowski
1

Clojure - 480 Bytes

Die Größe explodierte, aber zumindest ist es eine hübsche Zahl. Ich denke, es könnte viel verbessert werden, wenn man nur 1D-Vektor verwendet. Auf jeden Fall dauert der Testfall auf meinem Laptop etwas weniger als vier Sekunden. Ich dachte, es wäre angebracht, eine Funktion zu definieren, da es sich schließlich um eine funktionale Sprache handelt.

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

Beispiele:

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

Eine etwas ungolfedere (und schönere) Version:

(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)))
seequ
quelle
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}}

Probieren Sie es online!

Das Skript findet alle Lösungen für ein Sudoku.

Abgerollt:

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

}

Testfälle:

@(
    # 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
}
mazzy
quelle
0

D (322 Zeichen)

Für jedes ungelöste Quadrat erstellt es eine Reihe verfügbarer Optionen und durchläuft es dann in einer Schleife.

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

mit Leerzeichen:

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;
}
mleise
quelle
0

Perl (195 Zeichen)

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

Alle Kredit geht an den Schöpfer hier , und die Erklärung kann auch dort zu finden.

Qwix
quelle
1
Wenn Sie es nicht selbst geschrieben haben, sollten Sie den "Community Wiki" -Button überprüfen.
Kyle Kanos
Nach einigen Nachforschungen, was das ist, scheint es mir nicht möglich zu sein. Anscheinend sind 100 Wiederholungen erforderlich, damit ich das Kontrollkästchen sehen kann (siehe
Anhang
Hmm, war sich dieser Anforderung nicht bewusst.
Kyle Kanos
0

J, 94 Bytes

Arbeitet genauso wie die K-Version, nämlich mit einem BFS (soll also alle Lösungen ausgeben). Es werden Leerzeichen zwischen den Ausgabestellen ausgegeben, aber auch das K-Programm. Ich zähle nicht "s =:", da dies nur die Funktion benennt (so als würde ich den Dateinamen nicht in einer anderen Sprache zählen).

   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
Olius
quelle