Общий алгоритм решения задач, подобных Судоки, был разработан месье Лорьером в начале 70-х годов (см. книгу «Системы искусственного интеллекта», глава «Информационная система ALICE»). Суть его состоит в следующем. Задача представляется в виде описания конечных дискретных переменных и в виде набора ограничений. Система решения задач ALICE содержала в себе систему считывания условий задачи, систему ветвления (перебора) и набор полиномиальных алгоритмов, которые позволяют сокращать перебор. После считывания задачи система ALICE автоматически (!) применяет полиномиальные алгоритмы для удаления недопустимых значений переменных (см. в Интернете «Распространение ограничений», «Метод вперед-назад», «Интервальный анализ»). Если обнаружено противоречие – задача не имеет решения. Если все переменные зафиксированы – найдено единственное решение. Если есть незафиксированные переменные – нужно ветвиться (делать шаг перебора): делается ветвление и для каждой ветки начинается все сначала. Т.е. никакого антагонизма между «перебором» и «логическим решением» нет в природе. Просто, в силу отсталости нашего образования, под «логическим методом решения задач» неокрепшие умы называют систему сжатия доменов переменных и систему распространения ограничений. Если в системе решения задачи есть подходящие полиномиальные алгоритмы, то решение находится легко. Если в системе мало «подходящих» алгоритмов - доля перебора в решении задачи растет. Отсюда, кстати следует бессмысленность NP-классификации, т.к. важен не столько сам факт экспоненциального нарастания сложности, сколько сама доля перебора в решении задачи. Сам я непосредственно участвую в разработке системы Quick NP, в которой можно решить многие задачи Судоки. Предложенные в обсуждения задачи с 81-м числом имеют точно по одному решению. Quick NP использует 3 и 6 узлов дерева поиска соответственно чтобы полностью решить задачи. Если не считать неудачные узлы ветвления (сразу после ветвления становится ясно, что есть противоречие), то можно сказать, что Quick NP решает эти задачи без перебора. Т.е. если не считать за узел перебора действий типа «переменная x не может равняться a, так как получаем противоречие», то Quick NP обходится полностью без перебора. Ниже приведено формальное описание задачи на NP-языке, который используется Quick NP. Project Sudoki9 Module Main Field Lines () As IntegerCon Ins As ' Создание клеток__ForAll(i In Lines , Append(Check , Me:i , Dig :Lines (i )))Con InsFigs As ' Создание столбцов, строк и квадратов__ForAll(i In {1..9}, ____Append(Row , Me:i ) And Append(Col , Me:i ) And Append(Square , Me:i ))Class Row Var theChecks {} As Integer = Union(c In Check , c .y =Me, c )Con Injs As Inj(c In theChecks , Check (c ).d ) ' Цифры в строках уникальныClass Col Var theChecks {} As Integer = Union(c In Check , c .x =Me, c )Con Injs As Inj(c In theChecks , Check (c ).d ) ' Цифры в стобцах уникальныClass Square Var xStart As Integer = (Me-1) Mod 3 * 3 + 1Var yStart As Integer = (Me-1) \ 3 * 3 + 1Var theChecks {} As Integer = __Union(c In Check , Between(c .x , xStart , xStart +2) And____Between(c .y , yStart , yStart +2), c )Con Injs As Inj(c In theChecks , Check (c ).d ) ' Цифры в квадратах уникальныClass Check Field Dig As IntegerVar x As Integer = Me - 9*(y -1) ' Координата x клеткиVar y As Integer = (Me - 1) \ 9 + 1 ' Координата y клеткиVar d As If(Dig =0, {1..9}, {Dig }) ' Переменная цифры клеткиCon Save As Store(x , y , d )Data $InFile Main (Lines )__1, {8,3,0, 6,0,0, 0,0,1,______0,0,6, 0,3,0, 0,8,0,______0,0,1, 8,0,0, 0,6,0,______0,0,0, 9,0,0, 0,4,0,______0,0,3, 0,7,0, 2,0,0,______0,7,0, 0,0,5, 0,0,0,______0,1,0, 0,0,4, 6,0,0,______0,5,0, 0,9,0, 8,0,0,______9,0,0, 0,0,6, 0,2,3}EndДелая пошаговое выполнение, можно увидеть, как система Quick NP находит решение. Можно увидеть состояние стека перебора, отсеченные значения, журнал отсечения, само решение и т.п. Ниже приведено описание задачи ХИТОРИ на NP-языке Project Hitory Module Main Field Size As IntegerCon InsCols As ForAll(c In {1..Size }, Append(Col , Me:c ))Con StartConn As ' Начальная клетка поиска связности__If(Check (1).Used , Check (1).Conn , Check (2).Conn )Class Row Field Cols () As IntegerRef theChecks {} As Check .theRow Con InsChecks As ' Создание матрицы__ForAll(c In Cols , Append(Check , theRow :Me, theCol :c , v :Cols (c )))Con Unique As ' Не более одной уникального числа в строке__ForAll(i In {1..Size },____Sum(c In theChecks , c .v =i ) >= 2 ==>______Sum(c In theChecks , c .v =i , c .Used ) <= 1)Class Col Ref theChecks {} As Check .theCol Con Unique As__ForAll(i In {1..Size }, ' Не более одного уникального числа в столбце____Sum(c In theChecks , c .v =i ) >= 2 ==>______Sum(c In theChecks , c .v =i , c .Used ) <= 1)Class Check Field v As Integer ' Число в клеткеVar Used As Boolean ' Истино, если клетка не вычеркиваетсяVar Conn As Boolean ' Переменная связностиRef theCol As Col .theChecks ' КолонкаRef theRow As Row .theChecks ' СтолбецVar Neibs {} As Integer = ' Соседние клетки__If(theCol >1, {Me-1}, {}) Union If(theCol <Size , {Me+1},{}) ____Union If(theRow >1, {Me-Size }, {}) Union If(theRow <Size , {Me+Size }, {})Con EmptyNoConn As ' Не вычеркивать соседние клетки__ForAll(c In Neibs , c < Me ==> Check (c ).Used +Used >= 1)Con Conns As ' Обеспечение связности__Def(Conn ) And Dep(Conn ) And____If(Used , ForAll(i In Neibs , Check (i ).Conn ==> Conn ), Not Conn )Con Save As Store(theCol , theRow , Used )Data $InFile Main (Size )__1, 15EndRow (Cols )___1, { 9, 3, 6, 4, 8, 8, 15, 6, 12, 1, 5, 2, 12, 3, 15 }___2, { 13, 4, 4, 5, 6, 10, 13, 14, 7, 14, 2, 14, 1, 11, 12 }___3, { 7, 9, 5, 15, 11, 9, 1, 10, 9, 3, 12, 4, 6, 5, 8 }___4, { 3, 4, 9, 7, 14, 7, 13, 12, 2, 6, 6, 13, 4, 10, 8 }___5, { 12, 15, 8, 13, 7, 5, 6, 11, 5, 14, 3, 13, 5, 9, 2 }___6, { 14, 6, 12, 4, 2, 9, 4, 4, 3, 15, 6, 8, 7, 13, 10 }___7, { 11, 2, 4, 6, 15, 15, 2, 1, 5, 9, 8, 4, 10, 3, 13 }___8, { 6, 14, 1, 11, 5, 12, 8, 3, 8, 10, 10, 15, 4, 2, 4 }___9, { 8, 12, 8, 10, 7, 2, 11, 6, 14, 4, 10, 9, 15, 5, 7 }__10, { 5, 13, 15, 3, 13, 3, 14, 10, 6, 12, 7, 12, 11, 1, 4 }__11, { 6, 10, 3, 1, 8, 4, 7, 2, 11, 7, 14, 7, 7, 15, 4 }__12, { 2, 8, 7, 12, 1, 4, 5, 9, 13, 13, 15, 13, 14, 8, 3 }__13, { 12, 8, 4, 1, 13, 14, 12, 7, 13, 5, 11, 3, 14, 9, 9 }__14, { 10, 4, 14, 10, 9, 6, 3, 7, 1, 7, 4, 6, 2, 7, 15 }__15, { 2, 7, 2, 9, 12, 14, 8, 13, 10, 6, 4, 14, 3, 13, 11 } EndСистема Quick NP, используя 1735 узлов дерева поиска, обнаружила 31 решение. Ниже приведено одно из них (а также набор удаляемых пар, если я неправильно что-то набил в матрице): Код: Row(Cols) 1, { 9, , 6, 4, , 8, 15, , 12, 1, 5, 2, , 3, } 2, { 13, 4, , 5, 6, 10, , 14, 7, , 2, , 1, 11, 12 } 3, { 7, 9, 5, 15, 11, , 1, 10, , 3, 12, 4, 6, , 8 } 4, { 3, , 9, , 14, 7, , 12, 2, , 6, 13, 4, 10, } 5, { , 15, , 13, 7, , 6, 11, , 14, 3, , 5, , 2 } 6, { 14, 6, 12, , 2, 9, 4, , 3, 15, , 8, 7, 13, 10 } 7, { 11, , 4, 6, , 15, 2, 1, , 9, 8, , 10, , 13 } 8, { , 14, 1, 11, 5, 12, , 3, 8, , 10, 15, , 2, } 9, { 8, 12, , 10, , 2, 11, 6, 14, 4, , 9, 15, 5, 7 } 10, { 5, , 15, 3, 13, , 14, , 6, 12, 7, , 11, 1, 4 } 11, { 6, 10, 3, , 8, 4, , 2, 11, , 14, 7, , 15, } 12, { 2, , 7, 12, 1, , 5, 9, , 13, 15, , 14, 8, 3 } 13, { 12, 8, , 1, , 14, , 7, 13, 5, 11, 3, , 9, } 14, { 10, , 14, , 9, 6, 3, , 1, , 4, , 2, 7, 15 } 15, { , 7, 2, 9, 12, , 8, 13, 10, 6, , 14, 3, , 11 } End
Check(theCol, theRow) 2, 1 5, 1 8, 1 13, 1 15, 1 3, 2 7, 2 10, 2 12, 2 6, 3 9, 3 14, 3 2, 4 4, 4 7, 4 10, 4 15, 4 1, 5 3, 5 6, 5 9, 5 12, 5 14, 5 4, 6 8, 6 11, 6 2, 7 5, 7 9, 7 12, 7 14, 7 1, 8 7, 8 10, 8 13, 8 15, 8 3, 9 5, 9 11, 9 2, 10 6, 10 8, 10 12, 10 4, 11 7, 11 10, 11 13, 11 15, 11 2, 12 6, 12 9, 12 12, 12 3, 13 5, 13 7, 13 13, 13 15, 13 2, 14 4, 14 8, 14 10, 14 12, 14 1, 15 6, 15 11, 15 14, 15 End Технологии на месте не стоят, а многих из обсуждавшихся здесь проблем/вопросов, не существует в природе уже десятки лет.
|