Sudoku

Zadání Sudoku
Zadání Sudoku

Sudoku je populární logická hra, jejímž cílem je na plochu 9x9 polí doplnit chybějící číslice 1-9 takovým způsobem, aby se žádná neopakovala vícekrát v žádném řádku, sloupci ani čtverci (plocha se skládá z devíti čtverců velikosti 3x3 pole).

Autorem hlavolamu je Howard Garns, který jej publikoval v roce 1979 (pod názvem Number place). Hra se stala populární nejprve v Japonsku (vydávána od roku 1984) pod názvem Suuji wa dokushin ni kagiru a v roce 2005 se stala známou na celém světě.

Jak řešit Sudoku?

Sudoku se obvykle luští za pomoci tzv. kandidátů, což jsou všechna čísla, která mohou být v daném poli umístěna (na začátku všechna čísla 1-9). Kandidáti jsou poté postupně eliminováni za pomoci více či méně složitých myšlenkových postupů a konstrukcí. V okamžiku, kdy zbyde pouze jeden kandidát, tak je řešením pro dané pole.

Základní pravidla

Pro prvotní eliminaci kandidátů poslouží základní pravidla Sudoku. V žádné řádce, sloupci ani skupině nesmí být žádné číslo vícekrát. Ačkoliv je tato strategie velmi jednoduchá, tak postačuje pro většinu jednoduchých Sudoku.

(Hidden) Singles

Hidden single
Hidden single

Singles jsou ta políčka, pro která existuje pouze jeden kandidát, který je jejich řešením. Hidden single je takový kandidát, který je jedinečný v rámci dané řádky, sloupce nebo skupiny, ale není v daném políčku osamocený. Přesto je díky základním pravidlům řešením tohoto políčka.

Naked pair
Naked pair

Naked pair

Pokud dvě políčka v rámci skupiny obsahují pouze totožnou dvojici kandidátů, pak žádné další pole v této skupině nesmí dané kandidáty obsahovat (pokud by tomu tak nebylo, tj. některý kandidát by byl řešením jiného pole, tak by nešlo původní políčka vyplnit). Tuto strategii lze analogicky použít pro trojice kandidátů a tři políčka, čtveřice a čtyři políčka a tak dále.

Pointing pair
Pointing pair

Pointing pairs, Pointing tripples (intersection removal)

Za předpokladu, že se některý kandidát vyskytuje vícekrát v rámci jednoho řádku nebo sloupce skupiny (ale ne v rámci více sloupců/řádků), tak jej můžeme odstranit z průsečíku tohoto řádku/sloupce v ostatních skupinách, kterými tento řádek/sloupec prochází.

Box/Line reduction (intersection removal)

Pokud předchozí postup obrátíme naruby, tak dostaname Box/Line reduction strategii. Mějme řádek nebo sloupec, který má určité kandidáty pouze v jedné skupině, pak můžeme tyto kandidáty vyškrtnout ze všech ostatních polí dané skupiny.

Box/Line reduction
Box/Line reduction

Strojové řešení a generování Sudoku

Při generování nového zadání je zapotřebí řešit Sudoku ve dvou rovinách. Tou první je ověření jednoznačnosti zadání, které provedeme pomocí backtrackingu. Backtracking je metoda organizovaných pokusů a omylů, při které zkusíme na dané pole vložit nějakou hodnotu a zkontrolujeme, jestli toto částečné řešení neodporuje pravidlům. Pokud je vše v pořádku, zkusíme to znovu u dalšího pole. V případě selhání se vrátíme na místo posledního omylu a opravíme jej novým pokusem. Tímto způsobem zaručeně najdeme všechna řešení, která zadání umožňuje. Nevýhodou je velká výpočetní náročnost tohoto postupu (ručně by se dala tato metoda aplikovat jen na velmi omezené podproblémy).

Druhou rovinou je ověření praktické řešitelnosti daného Sudoku. To provedeme pouze za pomoci lidmi použitelných technik, z nichž některé jsme uvedli výše. Tímto způsobem také můžeme snadno stanovit obtížnost řešení na základě počtu potřebných tahů nebo obtížnosti nutných strategií.

Generování zadání

V předchozích odstavcích jsme předpokládali existenci zadání, ale neuvedli jsme si, jakým způsobem jej lze získat. V praxi se používají především dva postupy. První z nich vygeneruje náhodné konzistentní úplné řešení Sudoku a postupně z něj odebírá vyplněná řešení jednotlivých polí. Takto postupuje tak dlouho, dokud má Sudoku jednoznačné řešení, případně dokud obtížnost řešení nepřesáhne nějaký práh.

Druhý přístup je zcela opačný. Algoritmus nejprve vygeneruje zadání, v němž je konzistentně vyplněno 17 polí (nejmenší známý počet polí, pro nějž bylo nalezeno zadání, které má unikátní řešení). Poté procedura postupně vyplňuje náhodná pole a kontroluje konzistenci zadání. V okamžiku, kdy má dané zadání pouze jedno řešení, jehož obtížnost nepřesáhne daný práh, procedura terminuje.

Kód

/**
 * Resic Sudoku pomoci backtrackingu. Tato metoda nezjisti, jestli je dane
 * sudoku resitelne clovekem, ale zda-li ma dane zadani vubec reseni, pripadne
 * kolik takovychto reseni umoznuje (zjisti a vypise vsechna reseni).
 * @author Pavel Micka
 */
public class SudokuSolver {
    /**
     * Velikost ulohy == klasicke Sudoku 9*9, 3x vnitrni ctverec 3*3, kazde cislo
     * (1-9) pouze jednou v radku, sloupci i ctverci
     */
    public static final int SUDOKU_SIZE = 9;
    /**
     * Velikost vnitrnich ctvercu (3*3 dle beznych pravidel)
     */
    public static final int SQUARE_SIZE = 3;
    /**
     * Prazdne pole
     */
    public static final int EMPTY = 0;

    /**
     * Vyresi zadane Sudoku pomoci backtrackingu (najde vsechna reseni)
     * @param array pole zadani
     */
    public static void solveSudoku(int[][] array) {
        if(array.length != SUDOKU_SIZE) throw new IllegalArgumentException("Pole ma mit velikost 9x9");

        boolean[][] fixed = new boolean[SUDOKU_SIZE][SUDOKU_SIZE];
        for (int i = 0; i < array.length; i++) {
            if (array[i].length != SUDOKU_SIZE) throw new IllegalArgumentException("Pole ma mit velikost 9x9");
            for (int j = 0; j < array.length; j++) {
                if (array[i][j] != 0) fixed[i][j] = true;

            }
        }

        //prejdeme na prvni nepredvyplnenou souradnici
        int x = -1;
        int y = 0;
        do {
            x = x + 1; //posun o souradnici dal
            boolean overflow = x >= SUDOKU_SIZE; //pretekl radek?
            if (overflow) {
                x = 0;
                y += 1;
            }
        } while(fixed[y][x]); //dokud je pole zablokovane (soucasti zadani)

        for (int i = 1; i <= SUDOKU_SIZE; i++) {
            solve(array, fixed, x, y, i);
        }
    }
    /**
     * Resi Sudoku
     * @param array pole reseni
     * @param fixed pole, ve kterem true znamena, ze jde o hodnotu ze zadani,
     * @false, ze jde o hodnotu resitele
     * @param x souradnice x, na kterou se bude pridavat dalsi hodnota
     * @param y souradnice y, na kterou se bude pridavat dalsi hodnota
     * @param value hodnota
     */
    private static void solve(int[][] array, boolean[][] fixed, int x, int y, int value) {
        if (!checkConsistency(array, x, y, value)) return; //reseni neni konzistentni
        array[y][x] = value; //set
        do {
            x = x + 1; //posun o souradnici dal
            boolean overflow = x >= SUDOKU_SIZE; //pretekl radek?
            if (overflow) {
                x = 0;
                y += 1;
                if (y == SUDOKU_SIZE) { //pretekl sloupec...konec reseni
                    printArray(array);
                    return;
                }
            }
        } while(fixed[y][x]);//dokud je pole zablokovane (soucasti zadani)
        for (int i = 1; i <= SUDOKU_SIZE; i++) { //backtrack
            solve(array, fixed, x, y, i);
        }
        array[y][x] = EMPTY; //reset policka (aby neprekazelo pri backtracku)
    }
    /**
     * Zkontroluje, jestli je reseni stale korektni
     * @param array pole reseni
     * @param x x souradnice, na kterou je pridavana hodnota
     * @param y y souradnice, na kterou je pridavana hodnota
     * @param value hodnota
     * @return true - pokud je reseni konzistentni, false - pokud reseni neni konzistentni
     */
    private static boolean checkConsistency(int[][] array, int x, int y, int value) {
        //Sloupec
        for (int i = 0; i < array.length; i++) {
            if (i != y && array[i][x] == value) return false;
        }
        //radek
        for (int i = 0; i < array[y].length; i++) {
            if (i != x && array[y][i] == value) return false;
        }
        //ctverec
        int vertical = y/SQUARE_SIZE; //o kolikaty vertikalni ctverec se jedna
        int horizontal = x/SQUARE_SIZE; //o kolikaty horizontalni ctverec se jedna

        for (int i = vertical*SQUARE_SIZE; i < vertical*SQUARE_SIZE + SQUARE_SIZE; i++) {
            for (int j = horizontal*SQUARE_SIZE; j < horizontal*SQUARE_SIZE + SQUARE_SIZE; j++) {
                if (array[i][j] == value) return false;
            }
        }
        return true;
    }
    /**
     * Vypise pole Sudoku
     * @param array pole sudoku
     */
    private static void printArray(int[][] array) {
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++) {
                System.out.print(array[i][j] + "|");
            }
            System.out.println("");
        }
        System.out.println("");
    }
}
/**
 * Resicka sudoku vyuzivajici "lidske" strategie
 * @author Pavel Micka
 */
public class HumanizedSudokuSolver {
    private SudokuField[][] sudoku;
    private SudokuStrategy[] strategies;

    /**
     * Zkonstruuje resicku pro zadane sudoku
     * @param setting zadani sudoku, 0 znaci prazdne pole
     * @param strategies pole strategii, ktere maji byt pouzity
     */
    HumanizedSudokuSolver(int[][] setting, SudokuStrategy... strategies) {
        this.strategies = strategies;
        sudoku = new SudokuField[setting.length][setting.length];
        for (int i = 0; i < setting.length; i++) {
            for (int j = 0; j < setting[i].length; j++) {
                if (setting[i][j] == 0) {
                    sudoku[i][j] = new SudokuField();
                } else {
                    sudoku[i][j] = new SudokuField(setting[i][j]);
                }
            }
        }
    }
    /**
     * Vrati reseni sudoku
     * @return
     */
    public int[][] solve() {
        boolean succ;
        do{
            succ = false;
            for (SudokuStrategy s : strategies) {
                succ = s.solve(sudoku);
                if (succ) {
                    break;
                }
            }
        } while(succ);

        int[][] solution = new int[sudoku.length][sudoku.length];
        for (int i = 0; i < sudoku.length; i++) {
            for (int j = 0; j < sudoku.length; j++) {
                solution[i][j] = sudoku[i][j].getSolution();
            }
        }

        return solution;


    }



    /**
     * Testovaci main metoda
     * Program vyresi, co umi, coz jsou s temito strategiemi cca. az pokrocila
     * Sudoku a vypise vysledek
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int[][] setting = { //MF DNES 23.8.2010, priloha leto
            {0, 0, 4, 0, 3, 6, 9, 2, 7},
            {1, 0, 0, 0, 0, 5, 0, 0, 0},
            {0, 0, 0, 2, 0, 0, 0, 0, 4},
            {0, 0, 5, 0, 0, 0, 0, 6, 0},
            {6, 4, 0, 0, 0, 0, 0, 8, 5},
            {0, 7, 0, 0, 0, 0, 2, 0, 0},
            {5, 0, 0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 7, 0, 0, 0, 0, 2},
            {4, 3, 7, 9, 2, 0, 5, 0, 0}
        };
        SudokuStrategy[] s = {new OneInARowStrategy(), new OneInAColumnStrategy(), new OneInAGroupStrategy()};
        HumanizedSudokuSolver solver = new HumanizedSudokuSolver(setting, s);
        int[][] solution = solver.solve();
        for (int i = 0; i < solution.length; i++) {
            for (int j = 0; j < solution.length; j++) {
                System.out.print(solution[i][j] + " ");
            }
            System.out.println("");
        }
    }
}

/**
 * Specifikuje metodu, kterou by mely mit vsechny strategie pro reseni Sudoku
 * @author Pavel Micka
 */
interface SudokuStrategy {

    /**
     * Provede jeden krok reseni Sudoku
     * @param sudoku pole obsahujici sudoku, 0 oznacuje prazdna mista
     * @return true pokud se povedlo krok provest, false v opacnem pripade
     */
    public boolean solve(SudokuField[][] sudoku);
}

/**
 * Strategie, ktera rika, ze v jednom radku smi byt dane cislo pouze jednou
 * @author Pavel Micka
 */
class OneInARowStrategy implements SudokuStrategy {

    public boolean solve(SudokuField[][] sudoku) {
        for (int i = 0; i < sudoku.length; i++) {
            for (int j = 0; j < sudoku[i].length; j++) {
                int solution = sudoku[i][j].getSolution();
                if (solution != 0) { //pokud pole neni vyreseno
                    for (int k = 0; k < sudoku[i].length; k++) { //overme radku na kandidaty
                        if (sudoku[i][k].getSolution() == 0) { //pokud dane pole neni vyreseno
                            if (sudoku[i][k].hasCandidate(solution)) { //a pokud ma kandidata
                                sudoku[i][k].removeCandidate(solution); //tak kandidata odstranime
                                System.out.println("OneInARow: Odstranuji kandidata " + solution
                                        + " na souradnicich x: " + k + " y: " + i);
                                return true; //a vratime uspech
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
}

/**
 * Strategie, ktera rika, ze v jednom sloupci smi byt dane cislo pouze jednou
 * @author Pavel Micka
 */
class OneInAColumnStrategy implements SudokuStrategy {

    public boolean solve(SudokuField[][] sudoku) {
        for (int i = 0; i < sudoku.length; i++) {
            for (int j = 0; j < sudoku[i].length; j++) {
                int solution = sudoku[j][i].getSolution();
                if (solution != 0) { //pokud pole neni vyreseno
                    for (int k = 0; k < sudoku.length; k++) { //overme sloupec na kandidaty
                        if (sudoku[k][i].getSolution() == 0) { //pokud dane pole neni vyreseno
                            if (sudoku[k][i].hasCandidate(solution)) { //a pokud ma kandidata
                                sudoku[k][i].removeCandidate(solution); //tak kandidata odstranime
                                System.out.println("OneInAColumn: Odstranuji kandidata " + solution
                                        + " na souradnicich x: " + i + " y: " + k);
                                return true; //a vratime uspech
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
}

/**
 * Strategie, ktera rika, ze v jedne skupine muze byt dane cislo pouze jednou
 * @author Pavel Micka
 */
class OneInAGroupStrategy implements SudokuStrategy {

    public boolean solve(SudokuField[][] sudoku) {
        int groupCount = (int) Math.sqrt(sudoku.length); //pocitame se ctvercovym sudoku, pocet skupin na radku a sloupec
        for (int i = 0; i < sudoku.length; i++) {
            for (int j = 0; j < sudoku[i].length; j++) {
                int solution = sudoku[i][j].getSolution();
                if (solution != 0) { //pokud pole neni vyreseno
                    for (int k = i - (i % groupCount); k < i - (i % groupCount) + groupCount; k++) {//radky
                        for (int l = j - (j % groupCount); l < j - (j % groupCount) + groupCount; l++) {//sloupce
                            if (sudoku[k][l].getSolution() == 0){ //pokud pole jeste neni vyreseno
                                if (sudoku[k][l].hasCandidate(solution)){ //a ma odpovidajiciho kandidata
                                    sudoku[k][l].removeCandidate(solution);
                                System.out.println("OneInAGroup: Odstranuji kandidata " + solution
                                        + " na souradnicich x: " + l + " y: " + k);
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
}

/**
 * Trida policka sudoku
 * @author Pavel Micka
 */
class SudokuField {

    /**
     * Mnozina kandidatu
     */
    private Set<Integer> candidates;

    /**
     * Vytvori policko, kandidaty jsou cisla 1-9
     */
    public SudokuField() {
        this.candidates = new HashSet<Integer>();
        for (int i = 1; i <= 9; i++) {
            candidates.add(i);
        }
    }

    /**
     * Vytvori policko s jedinym kandidatem (resenim)
     * @param nr reseni
     */
    public SudokuField(int nr) {
        this.candidates = new HashSet<Integer>();
        candidates.add(nr);
    }

    /**
     * Zjisti, jestli je dane cislo kandidatem tohoto pole
     * @param nr cislo
     * @return true pokud je cislo kandidatem, false jinak
     */
    public boolean hasCandidate(int nr) {
        return candidates.contains(nr);
    }
    /**
     * Odstrani kandidata
     * @param nr kandidat
     */
    public void removeCandidate(int nr) {
        boolean succ = candidates.remove(nr);
        assert succ;
    }

    /**
     * Vrati cislo, ktere je resenim tohoto policka
     * @return cislo, ktere je resenim, 0 pokud jeste pole nebylo vyreseno
     */
    public int getSolution() {
        if (candidates.size() != 1) {
            return 0;
        } else {
            return (Integer) (candidates.toArray()[0]);
        }
    }
}

/**
 * Resic Sudoku pomoci backtrackingu. Tato metoda nezjisti, jestli je dane
 * sudoku resitelne clovekem, ale zda-li ma dane zadani vubec reseni, pripadne
 * kolik takovychto reseni umoznuje (zjisti a vypise vsechna reseni).
 * @author Pavel Micka
 */
class SudokuSolver {
public:
    /**
     * Velikost ulohy == klasicke sudoku 9*9, 3x vnitrni ctverec 3*3, kazde cislo
     * (1-9) pouze jednou v radku, sloupci i ctverci
     */
    static const int SUDOKU_SIZE = 9;
    /**
     * Velikost vnitrnich ctvercu (3*3 dle beznych pravidel)
     */
    static const int SQUARE_SIZE = 3;
    /**
     * Prazdne pole
     */
    static const int EMPTY = 0;

    /**
     * Vyresi zadane Sudoku pomoci backtrackingu (najde vsechna reseni)
     * @param array pole zadani
     */
    static void solveSudoku(int ** array) {
        bool ** fixed = new bool *[SUDOKU_SIZE];
        for (int i = 0; i < SUDOKU_SIZE; i++) {
            fixed[i] = new bool[SUDOKU_SIZE];
        }
        
        for (int i = 0; i < SUDOKU_SIZE; i++) {
            for (int j = 0; j < SUDOKU_SIZE; j++) {
                if (array[i][j] != 0) fixed[i][j] = true;
                else fixed[i][j] = false;
            }
        }

        //prejdeme na prvni nepredvyplnenou souradnici
        int x = -1;
        int y = 0;
        do {
            x = x + 1; //posun o souradnici dal
            bool overflow = x >= SUDOKU_SIZE; //pretekl radek?
            if (overflow) {
                x = 0;
                y += 1;
            }
        } while (fixed[y][x]);//dokud je pole zablokovane (soucasti zadani)

        for (int i = 1; i <= SUDOKU_SIZE; i++) {
            solve(array, fixed, x, y, i);
        }

        //dealokace
        for (int i = 0; i < SUDOKU_SIZE; i++) delete fixed[i];
        delete[] fixed;

    }
private:
    /**
     * Resi Sudoku
     * @param array pole reseni
     * @param fixed pole, ve kterem true znamena, ze jde o hodnotu ze zadani,
     * @false, ze jde o hodnotu resitele
     * @param x souradnice x, na kterou se bude pridavat dalsi hodnota
     * @param y souradnice y, na kterou se bude pridavat dalsi hodnota
     * @param value hodnota
     */
    static void solve(int ** array, bool ** fixed, int x, int y, int value) {
        if (!checkConsistency(array, x, y, value)) return; //reseni neni konzistentni
        array[y][x] = value; //set
        do {
            x = x + 1; //posun o souradnici dal
            bool overflow = x >= SUDOKU_SIZE; //pretekl radek?
            if (overflow) {
                x = 0;
                y += 1;
                if (y == SUDOKU_SIZE) { //pretekl sloupec...konec reseni
                    printArray(array);
                    return;
                }
            }
        } while(fixed[y][x]);//dokud je pole zablokovane (soucasti zadani)
        for (int i = 1; i <= SUDOKU_SIZE; i++) { //backtrack
            solve(array, fixed, x, y, i);
        }
        array[y][x] = EMPTY; //reset policka (aby neprekazelo pri backtracku)
    }
    /**
     * Zkontroluje, jestli je reseni stale korektni
     * @param array pole reseni
     * @param x x souradnice, na kterou je pridavana hodnota
     * @param y y souradnice, na kterou je pridavana hodnota
     * @param value hodnota
     * @return true - pokud je reseni konzistentni, false - pokud reseni neni konzistentni
     */
    static bool checkConsistency(int ** array, int x, int y, int value) {
        //Sloupec
        for (int i = 0; i < SUDOKU_SIZE; i++) {
            if (i != y && array[i][x] == value) return false;
        }
        //radek
        for (int i = 0; i < SUDOKU_SIZE; i++) {
            if (i != x && array[y][i] == value) return false;
        }
        //ctverec
        int vertical = y/SQUARE_SIZE; //o kolikaty vertikalni ctverec se jedna
        int horizontal = x/SQUARE_SIZE; //o kolikaty horizontalni ctverec se jedna

        for (int i = vertical*SQUARE_SIZE; i < vertical*SQUARE_SIZE + 3; i++) {
            for (int j = horizontal*SQUARE_SIZE; j < horizontal*SQUARE_SIZE + 3; j++) {
                if (array[i][j] == value) return false;
            }
        }
        return true;
    }
    /**
     * Vypise pole Sudoku
     * @param array pole sudoku
     */
    static void printArray(int ** array) {
        for (int i = 0; i < SUDOKU_SIZE; i++) {
            for (int j = 0; j < SUDOKU_SIZE; j++) {
                cout << array[i][j] << "|";
            }
            cout << "\n";
        }
        cout << "\n";
    }
};

Litaratura

  • STUART, Andrew. Sudokuwiki.org/ [online]. 2008 [cit. 2010-08-24]. Dostupné z WWW: <http://www.sudokuwiki.org/>.
  • JOHNSON, Angus. Angus johnson's homepage [online]. 2005 [cit. 2010-08-24]. SOLVING SUDOKU. Dostupné z WWW: <http://www.angusj.com/sudoku/hints.php>.
{ zpětná vazba }
Delicious Delicious
Sdílet
Hodnocení (4): 4

Přečtěte si také

Diskuse





Polo13.12.2010
Zdravim, potřeboval bych od vás píchnout jelikož jsem začátečník v program. JAVA. Týka se to sudoku mým úkolem je naprogramovat generator sudoku. Co jsem zatim dokazal je vytvorit 2rozmerne pole a naplnit ho nahodnyma cislema a vypsat je. Problem nevim jak napsat podminku pro radky a sloupce. Nekdo ochotnej mi vystvetli jak to naprogramovat?
Pavel Mička22.11.2010
To byla chyba formátování článku. V kódu používám generický parametr Integer, který je uzavřený ve špičatých závorkách (a nevyescapoval jsem je). Sytax highlighter, kterým zvýrarazňuji kód, si patrně to přebral jako XML, kterému chybí uzavírací tag (a doplnil ho). Děkuji za upozornění.
Vojtěch Sejkora22.11.2010
na konci Java(jako člověk) máte zajímavé tagy </integer></integer></integer> k čemu tam jsou?:-o
Pavel Mička13.11.2010
Děkuji za zpětnou vazbu a za odhalení chyby (už by to mělo být v pořádku).
Tundra12.11.2010
V ukázkových programech máte drobnou chybu. V programu označeném "Java (jako člověk)" u jednotlivých strategií vypisujete na standardní výstup kandidáta, který se odstraňuje:
System.out.println("OneInARow: Odstranuji kandidata " + solution + ""+ " na souradnicich x: " + j + " y: " + i);
U všech třech strategií jsou špatně uvedené souřadnice. Například u strategie OneInARow to nemá být "j, i", ale "k, i":
System.out.println("OneInARow: Odstranuji kandidata " + solution + ""+ " na souradnicich x: " + k + " y: " + i);
Jde o nepatrnou chybu, která se nepodepisuje na funkčnosti, ale když už o tom víme, není na škodu jí opravit.
Jinak děkuji za krásné vysvětlení, máte opravdu poměrně čitélný kód a to se cení :-).
eXander7.10.2010
Samozřejmě se dá vygenerovat Sudoku, které má více řešení. Ale v běžných časopisech se s ním rozhodně nepotkáte.
Jinak zajímavý článek pro zájemce, Sudoku v XSLT :)
http://ajwelch.blogspot.com/2006/03/dimitres-tuned-sudoku-solution.html
Pavel Mička11.5.2010
kucchem: co já vím, tak by to mělo mít vždy jenom jedno řešení...teď se sice nemůžu opřít o žádný konkrétní článek (protože bych to musel hledat), ale když jsem kdysi zkoumal, jak se to generuje, tak u generátorů byla vždy otázka jednoznačnosti řešená (většinou pomocí algoritmu, který je i v tomto článku).
A pokud se nemýlím, tak jsem to myslím i u pár kusů zkoušel a nikdy nevyšlo jako nejednoznačné (ale to samozřejmě není žádný důkaz).
Jinak obecně sudoku může mít mnoho řešení...na anglické wiki je to rozebrané poměrně do podrobna. A co jsem teď koukal, je prý je zapotřebí minimálně 17 čísel na to, aby byla šance, že řešení bude unikátní.
Čili závěr v jedné větě, neb koukám, že se v tom už trochu motám: je možné, že to bude mít více řešení (ale to je otázka na toho, co psal ten generátor pro dané noviny), ale dost o tom pochybuji, protože takovéto hlavolamy obecně by měly mít právě jedno řešení.
kucchem10.5.2010
Dobrý den. Měl jsem s kolegy diskusi na téma počet řešení sudoku. Tím myslím, že když máte v novinách vytištěné sudoku a zveřejněné jeho řešení, jestli je opravdu jen jediné nebo jich je víc. Mám za to, že je víc řešení, ale většinou člověk použije to, které je potom zveřejněno, protože to vyplývá z dedukce. Je to tak, nebo má každé sudoku jen jedno řešení?
Pavel Mička3.5.2010
Přesně tak...existuje řekněme 20 strategií, které lze použít při řešení sudoku (jeden ve čtverci, jeden v řádku, jeden ve sloupci...a pak složitější)...zadání ale může být takové, že s tím člověk, který nezná buď všechny tyto strategie (a ani to mu nemusí pomoci) nebo nemá v hlavě zásobník a nebacktrackuje, není schopen žádným způsobem hnout...což je nežádoucí situace, protože je to hlavolam pro lidi :-)
Čili zhruba to, co jste řekl :-)
Ondra3.5.2010
Hezky den, chci se zeptat, co to znamena "resitelnost" clovekem? Tim se mysli, ze dane sudoku neni mozne resit bez pouziti backtrackingu, tzn. ze v kazdem tahu neni jednoznacne dano, co se ma vepsat alespon do jednoho policka?