Autor | Zpráva | ||
---|---|---|---|
KarelPolacek Profil * |
#1 · Zasláno: 25. 2. 2016, 08:45:37
Ahoj,
snažím se napsat program, co bude řešit sudoku. Stavím na backtrace řešení. Napsal jsem celou třídu, co by měla sudoku řešit a enormně jednoduché kousky opravdu vyřeší, jenže tím enormně jednoduché myslím, že z 81 čísel chybí tak 5. Klasické sudoku, spadající do kategorie lehké (cca 40 chybějících čísel) už to rozhodně nezvládá. U těch co nezvládá vrací chybu Fatal error: Allowed memory size of 1342177280 bytes exhausted (tried to allocate 65488 bytes). Co jsem tak koukal na internetu a zkoumal, tak to vypadá na infinite loop. Jenže ať jsem hledal jak jsem hledal, nemohl jsem ho najít. Moje solve třída funguje následovně: Najít prázdné políčko Pokud dotaz na prázdné políčko vrátí true, tak už neexistuje volné políčko => sudoku je hotové Pokud je nalezené políčko prázdné, připravíme si číslo 1 Pokud vybrané políčko není prázdné, vezmeme číslo co tam je a přičteme 1 (Tato funkcionalita je kvůli vracení se k již vyplněným políčkům a zkoušením jiné možnosti.) Vyzkoušíme vložit připravené číslo, pokud s ním obsahuje sudoku chybu, zvětšíme ho o 1, pokud je zkoušené číslo 9 a nefunguje, tak se vracíme k předchozímu políčku (toto celé je while cyklus) Pokud vložené číslo nezpůsobilo chybu, zapíšeme ho Celý tento proces se zde spustí znovu sám v sobě (tady by asi mohlo docházet k infinite loop?) Zkouše jsem to pomocí die() debbugovat, ale prvních pár čísel fungovalo dobře, takže bych řekl, že princip řešení je ok. Kde bych mohla být chyba? <?php class sudokuSolver { private $line = 0; private $column = 0; private $sudokuOnlyForRead = array( array('', '', '6', '4', '7', '', '3', '', ''), array('', '', '4', '6', '5', '', '9', '', ''), array('3', '', '', '', '', '8', '', '4', ''), array('2', '6', '7', '3', '', '9', '5', '', ''), array('8', '', '', '2', '4', '7', '1', '', ''), array('', '', '', '', '', '', '', '', '7'), array('', '', '2', '', '6', '5', '4', '3', '1'), array('', '', '8', '9', '3', '1', '', '2', ''), array('', '3', '', '', '', '', '', '', ''), ); private $sudoku = array( array('', '', '6', '4', '7', '', '3', '', ''), array('', '', '4', '6', '5', '', '9', '', ''), array('3', '', '', '', '', '8', '', '4', ''), array('2', '6', '7', '3', '', '9', '5', '', ''), array('8', '', '', '2', '4', '7', '1', '', ''), array('', '', '', '', '', '', '', '', '7'), array('', '', '2', '', '6', '5', '4', '3', '1'), array('', '', '8', '9', '3', '1', '', '2', ''), array('', '3', '', '', '', '', '', '', ''), ); public function solve() { $nextEmptyCell = $this->findNextEmptyCell(); startForLastEmptyCell: if ($nextEmptyCell === true) { return $this->sudoku; } if (empty($this->getActualCell())) { $newNumber = 1; } else { $newNumber = $this->getActualCell()+1; } while ($this->isNewNumberValid($newNumber) == false) { $newNumber++; if ($newNumber == 10) { $this->findLastEmptyCell(); goto startForLastEmptyCell; } } $this->sudoku[$this->line][$this->column] = $newNumber; $this->solve(); } private function isValidInLine($number) { $searchedLine = $this->line; if (in_array($number, $this->sudoku[$searchedLine])) { return false; } return true; } private function isValidInColumn($number) { $searchedColumn = $this->column; foreach ($this->sudoku as $lines) { if ($lines[$searchedColumn] == $number) { return false; } } return true; } private function isValidInBox($number) { $searchedLine = $this->line; $searchedColumn = $this->column; $linesToInspect = $this->coordsToInspectInBox($searchedLine); $columnsToInspect = $this->coordsToInspectInBox($searchedColumn); foreach ($linesToInspect as $lineToInspect) { foreach ($columnsToInspect as $columnToInspect) { if ($number == $this->sudoku[$lineToInspect][$columnToInspect]) { return false; } } } return true; } private function isNewNumberValid($number) { if ($this->isValidInLine($number) && $this->isValidInColumn($number) && $this->isValidInBox($number)) { return true; } return false; } private function findNextEmptyCell() { $nextEmptyCell = ''; $searchedLine = $this->line; $searchedColumn = $this->column; while(empty($nextEmptyCell)) { if ($searchedColumn == 9) { $searchedColumn = 0; $searchedLine++; } if ($searchedLine == 9) { return true; } if(empty($this->sudoku[$searchedLine][$searchedColumn])) { $nextEmptyCell = array($searchedLine, $searchedColumn); $this->line = $searchedLine; $this->column = $searchedColumn; return $nextEmptyCell; } $searchedColumn++; } } private function findLastEmptyCell() { $lastEmptyCell = ''; $searchedLine = $this->line; $searchedColumn = $this->column; while(empty($lastEmptyCell)) { if ($searchedColumn == 0) { $searchedColumn = 8; --$searchedLine; } --$searchedColumn; if(empty($this->sudokuOnlyForRead[$searchedLine][$searchedColumn])) { $lastEmptyCell = array($searchedLine, $searchedColumn); $this->line = $searchedLine; $this->column = $searchedColumn; } } return $lastEmptyCell; } private function coordsToInspectInBox($coord) { if ($coord % 3 == 0) { return array($coord, $coord+1, $coord+2); } elseif ($coord % 3 == 1) { return array($coord-1, $coord, $coord+1); } elseif ($coord % 3 == 2) { return array($coord-2, $coord-1, $coord); } } private function getActualCell() { return $this->sudoku[$this->line][$this->column]; } } |
||
juriad Profil |
#2 · Zasláno: 25. 2. 2016, 09:51:42
Důvod, proč to nefunguje, je ten, že ty se skoro nikdy nevynoříš z té rekurze. Přidej si do do
solve parametr hloubka a uvidíš, jak ti roste. Ty potřebuješ tu hloubku omezit na nějakou uvěřítelnou hodnotu.
public function solve($hloubka) { echo $hloubka . "\n"; ... $this->solve($hloubka + 1); } Zahoď informaci o aktuálním políčku (proměnné $line a $column), tu si budeš pamatovat v lokální proměnné ve funkci solve. Nebudeš ani potřebovat funkce findNextEmptyCell, findLastEmptyCell a getActualCell. Zkusím ten kód upravit, mezitím si zkus vypisovat tu hloubku, schválně, jestli tě napadne, proč roste. |
||
juriad Profil |
#3 · Zasláno: 25. 2. 2016, 10:26:31
Zjednodušený program, který skutečně funguje:
<?php class SudokuSolver { private $sudoku; function __construct($sudoku) { $this->sudoku = $sudoku; } public function solve($line = 0, $column = 0) { # returns either true of false if ($line >= 9) { # whole sudoku has been filled in return true; } # empty cell contains 0 if (!empty($this->sudoku[$line][$column])) { # this cell is already filled in; we continue to next one return $this->solve($line + ($column + 1 >= 9), ($column + 1) % 9); } # now we know that [$line, $column] is an empty cell to be processed for ($number = 1; $number <= 9; $number++) { # we try all nine numbers if ($this->isNewNumberValid($line, $column, $number)) { # if number $number can be assigned, try it $this->sudoku[$line][$column] = $number; # and try to fill the rest of sudoku $solution = $this->solve($line + ($column + 1 >= 9), ($column + 1) % 9); if ($solution !== false) { # we found our solution, we can return return true; } # try different number in next iteration } } # no number works, we have to make the cell empty again $this->sudoku[$line][$column] = 0; # and go back one step return false; } private function isValidInLine($line, $number) { return !in_array($number, $this->sudoku[$line]); } private function isValidInColumn($column, $number) { foreach ($this->sudoku as $lines) { if ($lines[$column] == $number) { return false; } } return true; } private function isValidInBox($line, $column, $number) { # we move $line and $column to top left corner of the box $line -= $line % 3; $column -= $column % 3; # and iterate over the box for ($l = $line; $l < $line + 3; $l++) { for ($c = $column; $c < $column + 3; $c++) { if ($number == $this->sudoku[$l][$c]) { return false; } } } return true; } private function isNewNumberValid($line, $column, $number) { # no need for if return $this->isValidInLine($line, $number) && $this->isValidInColumn($column, $number) && $this->isValidInBox($line, $column, $number); } public function printSudoku() { echo "-------------------\n"; for ($l = 0; $l < 9; $l++) { for ($c = 0; $c < 9; $c++) { echo $this->sudoku[$l][$c]; echo " "; if ($c == 2 || $c == 5) { echo " "; } } echo "\n"; if ($l == 2 || $l == 5) { echo "\n"; } } echo "-------------------\n\n"; } } $sudoku1 = array( array(0, 0, 6, 4, 7, 0, 3, 0, 0), array(0, 0, 4, 6, 5, 0, 9, 0, 0), array(3, 0, 0, 0, 0, 8, 0, 4, 0), array(2, 6, 7, 3, 0, 9, 5, 0, 0), array(8, 0, 0, 2, 4, 7, 1, 0, 0), array(0, 0, 0, 0, 0, 0, 0, 0, 7), array(0, 0, 2, 0, 6, 5, 4, 3, 1), array(0, 0, 8, 9, 3, 1, 0, 2, 0), array(0, 3, 0, 0, 0, 0, 0, 0, 0), ); $s1 = new SudokuSolver($sudoku1); $s1->printSudoku(); if ($s1->solve()) { $s1->printSudoku(); } else { echo "Has no solution\n"; } |
||
KarelPolacek Profil * |
#4 · Zasláno: 26. 2. 2016, 18:20:03
Aha, chápu. Zanořovalo se to cca 200000x. Opravil jsem a použil jsem část Tvé úpravy. Děkuji moc za Tvůj čas.
|
||
Časová prodleva: 9 let
|
0