En programación con restricciones y resolución de problemas SAT , el retroceso (también conocido como retroceso no cronológico [ 1 ] o retroceso inteligente [ 2 ] ) es una mejora para los algoritmos de retroceso que reduce el espacio de búsqueda . Mientras que el retroceso siempre sube un nivel en el árbol de búsqueda cuando se han probado todos los valores de una variable, el retroceso puede subir más niveles. En este artículo, se utiliza un orden fijo de evaluación de variables , pero las mismas consideraciones se aplican a un orden de evaluación dinámico.
Un árbol de búsqueda visitado mediante retroceso regular.
Un salto hacia atrás: el nodo gris no se visita.
Definición
Cuando el retroceso ha probado todos los valores para una variable sin encontrar ninguna solución, reconsidera la última de las variables asignadas previamente, cambiando su valor o retrocediendo aún más si no hay otros valores que probar. Si es la asignación parcial actual y se han probado todos los valores para sin encontrar una solución, el retroceso concluye que no existe ninguna solución que extienda. El algoritmo entonces "sube" a , cambiando el valor de si es posible, retrocediendo de nuevo en caso contrario.
La asignación parcial no siempre es necesaria en su totalidad para demostrar que ningún valor de conduce a una solución. En particular, un prefijo de la asignación parcial puede tener la misma propiedad, es decir, existe un índice tal que no puede extenderse para formar una solución con cualquier valor de . Si el algoritmo puede demostrar este hecho, puede considerar directamente un valor diferente para en lugar de reconsiderarlo como lo haría normalmente.
Un ejemplo en el que la asignación actual a se ha intentado sin éxito con cada valor posible de . El retroceso vuelve a , intentando asignarle un nuevo valor.
En lugar de retroceder, el algoritmo realiza una elaboración adicional, demostrando que las evaluaciones , , y no forman parte de ninguna solución.
Como resultado, la evaluación actual de no forma parte de ninguna solución, y el algoritmo puede retroceder directamente a , probando un nuevo valor para él.
La eficiencia de un algoritmo de retroceso depende de cuán alto sea capaz de retroceder. Idealmente, el algoritmo podría saltar de a cualquier variable tal que la asignación actual a no pueda extenderse para formar una solución con ningún valor de . Si este es el caso, se dice que es un salto seguro .
Determinar si un salto es seguro no siempre es factible, ya que los saltos seguros se definen en términos del conjunto de soluciones, que es precisamente lo que el algoritmo intenta encontrar. En la práctica, los algoritmos de retroceso utilizan el índice más bajo que pueden demostrar eficientemente que corresponde a un salto seguro. Los distintos algoritmos emplean diferentes métodos para determinar la seguridad de un salto. Estos métodos tienen distintos costes, pero un mayor coste para encontrar un salto seguro de mayor nivel puede compensarse con una menor cantidad de búsqueda debido a la omisión de partes del árbol de búsqueda.
Retroceso en los nodos de las hojas
La condición más simple en la que es posible el retroceso es cuando se ha demostrado que todos los valores de una variable son inconsistentes sin ramificaciones adicionales. En la satisfacción de restricciones , una evaluación parcial es consistente si y solo si satisface todas las restricciones que involucran a las variables asignadas, e inconsistente en caso contrario. Puede darse el caso de que una solución parcial consistente no pueda extenderse a una solución completa consistente porque algunas de las variables no asignadas no pueden asignarse sin violar otras restricciones.
La condición en la que todos los valores de una variable dada son inconsistentes con la solución parcial actual se denomina callejón sin salida . Esto ocurre precisamente cuando la variable es una hoja del árbol de búsqueda (que corresponde a los nodos que solo tienen hojas como hijos en las figuras de este artículo).
El algoritmo de retroceso de John Gaschnig realiza un retroceso solo en los callejones sin salida de las hojas. [ 3 ] En otras palabras, funciona de manera diferente al retroceso solo cuando se ha probado cada valor posible y ha resultado inconsistente sin necesidad de ramificar sobre otra variable.
Se puede encontrar un salto seguro simplemente evaluando, para cada valor , el prefijo más corto de inconsistente con . En otras palabras, si es un valor posible para , el algoritmo comprueba la consistencia de las siguientes evaluaciones:
El índice más pequeño (el más bajo de la lista) para el cual las evaluaciones son inconsistentes sería un salto seguro si fuera el único valor posible para . Dado que cada variable generalmente puede tomar más de un valor, el índice máximo que resulta de la verificación para cada valor es un salto seguro, y es el punto donde salta el algoritmo de John Gaschnig.
En la práctica, el algoritmo puede verificar las evaluaciones anteriores al mismo tiempo que verifica la consistencia de .
Retroceso en nodos internos
El algoritmo anterior solo retrocede cuando se puede demostrar que los valores de una variable son inconsistentes con la solución parcial actual sin ramificaciones adicionales. En otras palabras, solo permite retroceder en los nodos hoja del árbol de búsqueda.
Un nodo interno del árbol de búsqueda representa una asignación de variable consistente con las anteriores. Si ninguna solución extiende esta asignación, el algoritmo anterior siempre retrocede: en este caso no se realiza ningún salto hacia atrás.
No es posible realizar saltos hacia atrás en nodos internos como en los nodos hoja. De hecho, si se requieren algunas evaluaciones de ramificación, es porque son consistentes con la asignación actual. Por lo tanto, la búsqueda de un prefijo que sea inconsistente con estos valores de la última variable no tiene éxito.
En estos casos, lo que demuestra que una evaluación no forma parte de una solución con la evaluación parcial actual es la búsqueda recursiva . En concreto, el algoritmo "sabe" que no existe ninguna solución a partir de este punto porque regresa a este nodo en lugar de detenerse tras haber encontrado una solución.
Este retorno se debe a varios callejones sin salida , puntos donde el algoritmo ha demostrado que una solución parcial es inconsistente. Para retroceder aún más, el algoritmo debe tener en cuenta que la imposibilidad de encontrar soluciones se debe a estos callejones sin salida. En particular, los saltos seguros son índices de prefijos que aún hacen que estos callejones sin salida sean soluciones parciales inconsistentes.
En este ejemplo, el algoritmo vuelve a , después de probar todos sus valores posibles, debido a los tres puntos cruzados de inconsistencia.
El segundo punto sigue siendo inconsistente incluso si los valores de y se eliminan de su evaluación parcial (nótese que los valores de una variable están en sus hijos).
Las demás evaluaciones inconsistentes siguen siendo así incluso sin , , y
El algoritmo puede retroceder ya que estas son las variables más bajas que mantienen todas las inconsistencias. Se probará un nuevo valor para .
En otras palabras, cuando se han probado todos los valores de , el algoritmo puede retroceder a una variable anterior siempre que la evaluación de verdad actual de sea inconsistente con todas las evaluaciones de verdad de en los nodos hoja que son descendientes del nodo .
Simplificaciones

Debido a la cantidad potencialmente alta de nodos que hay en el subárbol de , la información que es necesaria para retroceder de forma segura desde se recopila durante la visita a su subárbol. Encontrar un salto seguro se puede simplificar mediante dos consideraciones. La primera es que el algoritmo necesita un salto seguro, pero aún funciona con un salto que no es el salto seguro más alto posible.
La segunda simplificación consiste en que los nodos del subárbol que se han omitido mediante un salto hacia atrás pueden ignorarse al buscar un salto hacia atrás para . Más precisamente, todos los nodos omitidos mediante un salto hacia atrás desde el nodo hasta el nodo son irrelevantes para el subárbol con raíz en , y también son irrelevantes sus otros subárboles.
De hecho, si un algoritmo descendiera del nodo a través de una ruta pero retrocediera en su camino, podría haber ido directamente de a en su lugar. En efecto, el retroceso indica que los nodos entre y son irrelevantes para el subárbol con raíz en . En otras palabras, un retroceso indica que la visita a una región del árbol de búsqueda fue un error. Por lo tanto, esta parte del árbol de búsqueda puede ignorarse al considerar un posible retroceso desde o desde uno de sus ancestros.

Este hecho puede aprovecharse recopilando, en cada nodo, un conjunto de variables previamente asignadas cuya evaluación basta para demostrar que no existe solución en el subárbol con raíz en dicho nodo. Este conjunto se construye durante la ejecución del algoritmo. Al retroceder desde un nodo, se elimina la variable de este conjunto y se agrega al conjunto del destino del retroceso o salto hacia atrás. Dado que los nodos que se omiten en el salto hacia atrás nunca se retrocede, sus conjuntos se ignoran automáticamente.
Retroceso basado en grafos
La lógica del backjumping basado en grafos es que se puede encontrar un salto seguro comprobando cuáles de las variables están en una restricción con las variables instanciadas en los nodos hoja. Para cada nodo hoja y cada variable de índice instanciada allí, los índices menores o iguales a cuya variable está en una restricción con se pueden usar para encontrar saltos seguros. En particular, cuando se han probado todos los valores para , este conjunto contiene los índices de las variables cuyas evaluaciones permiten demostrar que no se puede encontrar ninguna solución visitando el subárbol con raíz en . Como resultado, el algoritmo puede retroceder al índice más alto en este conjunto.
El hecho de que los nodos omitidos mediante retroceso puedan ignorarse al considerar un retroceso posterior puede ser aprovechado por el siguiente algoritmo. Al retroceder desde un nodo hoja, se crea el conjunto de variables que están en restricción con él y se "envía" de vuelta a su padre, o ancestro en caso de retroceso. En cada nodo interno, se mantiene un conjunto de variables. Cada vez que se recibe un conjunto de variables de uno de sus hijos o descendientes, sus variables se añaden al conjunto mantenido. Al retroceder o retroceder desde el nodo, la variable del nodo se elimina de este conjunto, y el conjunto se envía al nodo que es el destino del retroceso o del retroceso. Este algoritmo funciona porque el conjunto mantenido en un nodo recopila todas las variables que son relevantes para probar la insatisfacibilidad en las hojas que son descendientes de este nodo. Dado que los conjuntos de variables solo se envían al retroceder desde los nodos, los conjuntos recopilados en los nodos omitidos mediante retroceso se ignoran automáticamente.
Retroceso basado en el conflicto
El retroceso basado en conflictos ( también conocido como retroceso dirigido por conflictos) es un algoritmo más refinado que, en ocasiones, permite realizar retrocesos de mayor alcance. Se basa en comprobar no solo la presencia común de dos variables en la misma restricción, sino también si dicha restricción ha provocado alguna inconsistencia. En concreto, este algoritmo recopila una de las restricciones violadas en cada hoja. En cada nodo, el índice más alto de una variable presente en una de las restricciones recopiladas en las hojas constituye un salto seguro.
Si bien la restricción violada elegida en cada hoja no afecta la seguridad del salto resultante, elegir restricciones con los índices más altos posibles aumenta la altura del salto. Por esta razón, el método de retroceso basado en conflictos ordena las restricciones de tal manera que se prefieren las restricciones sobre variables de índices más bajos a las restricciones sobre variables de índices más altos.
Formalmente, una restricción se prefiere sobre otra si el índice más alto de una variable en pero no en es menor que el índice más alto de una variable en pero no en . En otras palabras, excluyendo las variables comunes, se prefiere la restricción que tiene todos los índices más bajos.
En un nodo hoja, el algoritmo elige el índice más bajo que sea inconsistente con la última variable evaluada en la hoja. Entre las restricciones que se violan en esta evaluación, elige la más preferida y recopila todos sus índices menores que . De esta manera, cuando el algoritmo regresa a la variable , el índice más bajo recopilado identifica un salto seguro.
En la práctica, este algoritmo se simplifica al agrupar todos los índices en un único conjunto, en lugar de crear un conjunto para cada valor . En concreto, el algoritmo recopila, en cada nodo, todos los conjuntos provenientes de sus descendientes que no se hayan omitido mediante el retroceso. Al retroceder desde este nodo, este conjunto se elimina de la variable del nodo y se añade al destino del retroceso.
El backjumping dirigido por conflictos fue propuesto para problemas de satisfacción de restricciones por Patrick Prosser en su artículo fundamental de 1993. [ 4 ]
Véase también
Notas y referencias
Bibliografía
- Dechter, Rina (2003). Procesamiento de restricciones . Morgan Kaufmann Publishers . ISBN 1-55860-890-7.
- Gaschnig, John (1977). "Un algoritmo general de retroceso que elimina la mayoría de las pruebas redundantes" (PDF) . Actas de la 5.ª Conferencia Internacional Conjunta sobre Inteligencia Artificial (IJCAI-77) . Vol. 1. Cambridge, Massachusetts, EE. UU.: Conferencias Internacionales Conjuntas sobre Inteligencia Artificial. págs. 457–457 .
- Möhle, S.; Biere, A. (2019). "Backing backtracking". Theory and Applications of Satisfiability Testing – SAT 2019: 22nd International Conference, SAT 2019, Lisboa, Portugal, 9–12 de julio de 2019, Proceedings . Springer International Publishing. pp. 250–266 .
- Prosser, Patrick (1993). "Algoritmos híbridos para el problema de satisfacción de restricciones" (PDF) . Inteligencia Computacional .
- Programación con restricciones
- Algoritmos de búsqueda