Está en la página 1de 69

CAPITULO III: MÉTODOS DE SOLUCIÓN DE PROBLEMAS.

En el capítulo anterior estudiamos diferentes formas en que se puede representar


el conocimiento en los sistemas de I.A. En este capítulo analizaremos los distintos
métodos de solución de problemas (M.S.P.) que permitirán manipular el
conocimiento representado en esas formas.

3.1. Introducción.

Dado un sistema de I.A., donde el conocimiento está representado en una de las


formas estudiadas, se desea resolver un determinado problema de un dominio D
bajo un conjunto de ciertas condiciones C. Solucionar este problema significa
encontrar una solución x ∈ X que satisfaga el conjunto de condiciones C, donde X
es el conjunto de todas las soluciones posibles a dicho problema.

Este proceso de solucionar un problema se realiza mediante una búsqueda en el


espacio X. Existen tres esquemas básicos de solución de problemas:
- Esquema de producción.
- Esquema de reducción.
- Esquema de reducción débil.

Analicemos cada uno de estos esquemas.

Esquema de Producción: En este esquema el problema a solucionar se


representa en un espacio de estados y su solución se reduce a la búsqueda en
este espacio. Los conceptos principales en este esquema son:

- Estados: situaciones en que se pueden encontrar los objetos que


caracterizan al problema durante su solución.
- Movimientos: acciones legales que permiten pasar de un estado del problema
a otro.
- Función de evaluación de estados: asigna a un estado un estimado heurístico
del esfuerzo necesario para alcanzar una solución desde este
estado.
- Función de selección de movimientos: selecciona uno o más de los movimien-
tos aplicables.
- Función de selección de estados: selecciona el estado a partir del cual debe
continuar la búsqueda.

Encontrar la solución de un problema significa, entonces, descubrir algún camino


entre el estado inicial y el final. El procedimiento general de un esquema de
producción es:

1) Inicializar: Determinar, a partir del problema, el estado inicial S0 y colocarlo


como raíz del árbol de búsqueda. Aplicar la función de selección de
movimientos a S0 y generar sus estados descendientes.

59
2) Prueba: Examinar si el árbol de búsqueda contiene alguna solución, o sea, si
uno de los estados generados es el estado final, entonces
TERMINAR y EXITO. En caso contrario aplicar la función de
evaluación a cada uno de los estados generados.

3) Generar:
a) Si los recursos computacionales están agotados o no existen en el
árbol estados sin procesar, entonces TERMINAR y FALLO, en caso
contrario continuar.
b) Aplicar la función de selección de estados a los estados del árbol de
búsqueda para seleccionar uno de ellos.
c) Aplicar la función de selección de movimientos al estado
seleccionado.
d) Generar los nuevos estados aplicando los movimientos
seleccionados.
e) Repetir desde el paso de prueba.

La construcción de la función de evaluación de estados para un problema


concreto requiere del uso de conocimiento sobre ese problema. No siempre se
dispone de dicho conocimiento, por lo que no siempre es posible definir esta
función. La existencia o no de esta función trae por consecuencia que los métodos
de búsqueda se dividan en dos tipos:

- Búsqueda a ciegas: no se dispone de conocimiento para definir la función de


evaluación de estado.
- Búsqueda heurística: se puede definir la función de evaluación de estado.

Dentro de estos dos tipos existen diferentes métodos de búsqueda, los cuales
están determinados por la forma en que se construyen las funciones de selección
de estados y de movimientos. Entre estos métodos podemos señalar:

Métodos de búsqueda a ciegas (Brute-Force searches):


- Algoritmo del museo británico.
- Primero en profundidad (depth-first).
- Primero a lo ancho (breadth-first).
- Búsqueda en árboles y/o.
- Búsqueda en sistemas de producción.
- Búsqueda bidireccional.
- Búsqueda por diferencias.
- Búsqueda de soluciones múltiples.

Métodos de búsqueda heurística:


- Búsqueda por el incremento mayor (hill-climbing).
- Búsqueda por el mejor nodo o algoritmo A* (best-first).
- Búsqueda heurística en árboles y/o o algoritmo AO*.

Otros métodos de búsqueda pueden estudiarse en [Ric88].

60
Esquema de reducción: En este esquema el problema se descompone en varios
subproblemas, los cuales se resuelven de manera independiente y luego se
combinan sus soluciones para obtener la solución del problema original. Existen
dos tipos de movimientos: un conjunto de movimientos de reducción que se
encarga de la descomposición de un estado en otros que se supone sean más
fáciles de manejar que el original y el otro constituye un conjunto de movimientos
terminales que resuelven el problema completamente.

Los conceptos principales de este esquema, así como su procedimiento general


son similares a los del esquema de producción, pero las funciones de evaluación
de estado, selección de estados y selección de movimientos se basan en
consideraciones diferentes. Es usual representar los problemas que se resuelven
con este esquema mediante árboles y/o (and/or).

Esquema de reducción débil: Este esquema está orientado a tareas de


planificación.

3.2. Pasos para resolver un problema.

Para resolver un problema debemos seguir los siguientes pasos:

- Definir el problema con precisión: especificar el espacio del problema, los


operadores para moverse en dicho espacio y los estados inicial y final o
meta.
- Analizar el problema: determinar las características del problema para
seleccionar las técnicas que pueden resolverlo.
- Aislar y representar el conocimiento necesario para resolver el problema.
- Escoger la(s) mejor(es) técnica(s) y aplicarla(s) al problema particular.

3.2.1. Primer paso: definición precisa del problema.

El primer paso hacia el diseño de un sistema para resolver un problema es la


creación de una descripción formal y manejable de dicho problema, o sea, la
definición del espacio de búsqueda de las soluciones.

3.2.1.1. Espacios de búsqueda.

Un espacio de búsqueda no es más que el ambiente o espacio de todas las


soluciones posibles donde se realiza la búsqueda de una solución. Está formado
por un conjunto de nodos que constituyen soluciones parciales o posibles del
problema y un conjunto de operadores que permiten movernos de un nodo a otro.
El proceso de búsqueda de una solución consiste, entonces, en encontrar una
secuencia de operadores que transformen el nodo inicial en el final. Existen tres
tipos de espacios de búsqueda:

- Espacio de estado.
- Espacio de reducción de problemas.
- Árboles de juego.

61
Analicemos cada uno de estos tipos.

Espacio de estado.

Veamos un ejemplo:
Consideremos el problema de “jugar al ajedrez”. Nuestro objetivo no sólo es jugar
la partida, sino ganar la misma. Es necesario, en primer lugar, especificar cuál es
la posición inicial del tablero de ajedrez (nodo inicial), las reglas que definen los
movimientos legales en este juego y qué posiciones del tablero representan una
victoria para un jugador u otro (nodos finales). Además debemos hacer explícita la
meta de no solo jugar una partida de ajedrez, sino también ganar dicha partida si
podemos. Una forma de describir la posición inicial del tablero puede ser
mediante una matriz de 8x8, donde cada elemento es un símbolo que representa
la pieza del ajedrez que está situada en la posición de apertura del ajedrez.
Nuestra meta podría ser tratar de llegar a una posición del tablero, en la cual el
oponente no pueda realizar ningún movimiento legal y su rey esté amenazado.
Los movimientos legales proporcionan la manera de llegar desde el estado inicial
hasta el estado meta. Pueden describirse como un conjunto de reglas donde la
parte izquierda describa una posición del tablero que debe coincidir con la actual
y la parte derecha describa la nueva posición del tablero una vez ejecutado dicho
movimiento. Existen diversas maneras de describir estas reglas:

1) Tableroi Tableroj

donde Tableroi es una matriz de 8x8 que representa, por ejemplo, la posición
inicial del tablero y Tableroj es otra matriz de 8x8 que representa, por ejemplo, el
tablero del ajedrez con todas sus piezas en su posición inicial y el movimiento
P4R realizado.

Como puede verse, esta representación requiere una regla separada para cada
120
una de las 10 posiciones posibles del tablero, lo cual presenta tres dificultades:
nadie puede proporcionar el conjunto de tales reglas, pues esto requeriría mucho
tiempo, es difícil lograrlo sin cometer errores y ningún programa puede manejar
fácilmente todas esas reglas aunque se usen tablas hash para encontrar de forma
rápida las reglas relevantes para cada movimiento. El solo hecho de almacenar
tantas reglas crea dificultades.

2) Peón blanco en casilla(columna 4, fila 2) Mover peón blanco


y desde casilla
Casilla(columna 4, fila 3) está vacía (columna 4,fila 2)
y a casilla(columna
Casilla(columna 4, fila 4) está vacía 4,fila 4)

Esta representación minimiza los problemas mencionados en la forma anterior.


Mientras más claro podamos escribir las reglas, menos trabajo tendremos para
proporcionarlas y más eficiente será el programa que las use.

Realmente lo que hemos hecho es definir el problema de jugar al ajedrez como un


problema de movimientos a través de un espacio de estado, donde cada estado

62
corresponde a una posición válida del tablero. Podemos jugar al ajedrez
empezando en un estado inicial, usando un conjunto de reglas para movernos de
un estado a otro, intentando finalizar en uno de entre un conjunto de estados
finales.

En cada momento el proceso de resolución del problema puede encontrarse en un


estado particular. El conjunto de todos los estados posibles para un problema
concreto se denomina espacio de estado. Definir un problema significa,
entonces, especificar:
- el espacio de estados que contiene todas las configuraciones posibles de los
objetos relevantes y el conjunto de operadores que permiten movernos en
dicho espacio. Naturalmente es posible definir este espacio sin enumerar,
explícitamente, todos los estados que contiene, como veremos más adelante.
- el estado inicial.
- uno o más estados finales que serán aceptados como solución al problema.

Veamos otro ejemplo. Supongamos que queremos encontrar en un mapa la ruta


de una ciudad a otra. En este caso, el espacio de estados es la colección de todas
las ciudades. El estado inicial sería la ciudad de partida y el estado final es la
ciudad de llegada. Las restantes ciudades representan estados intermedios del
problema. Los operadores para moverse de un estado a otro son precisamente los
caminos existentes entre las ciudades. El proceso de encontrar la ruta consistiría,
nuevamente, en la búsqueda de un camino entre el estado inicial y el final.

Analicemos ahora un ejemplo diferente a los anteriores: el problema de los jarros


de agua, el cual consiste en que se tienen dos jarros de agua de 3 y 4 litros
respectivamente. Ninguno de los jarros tiene marca de medición y se puede usar
una bomba para llenar de agua los mismos. ¿Cómo poner exactamente 2 litros de
agua en el jarro de 4 litros?.

El espacio de estados puede describirse como el conjunto de pares de enteros (x,y)


tales que x=0,1,2,3,4 y y=0,1,2,3; x representa el número de litros de agua del
jarro de 4 litros y y representa el número de litros de agua del de 3 litros. El
estado inicial es (0,0), pues inicialmente los jarros están vacíos y el estado final es
(2,n) para cualquier valor de n, pues el problema no especifica cuántos litros
deben quedar en el jarro de 3 litros. Los operadores para moverse de un estado a
otro pueden describirse de la siguiente manera:
1. (X,Y | X<4) → (4,Y) Llenar el jarro de 4 litros.
2. (X,Y | Y<3) → (X,3) Llenar el jarro de 3 litros.
3. (X,Y |X>0) → (X-D,Y) Verter D litros de agua del jarro de 4 litros.
4. (X,Y | Y>0) → (X,Y-D) Verter D litros de agua del jarro de 3 litros.
5. (X,Y | X>0) → (0,Y) Vaciar el jarro de 4 litros en el suelo.
6. (X,Y | Y>0) → (X,0) Vaciar el jarro de 3 litros en el suelo.
7. (X,Y | X+Y≥4 y Y>0) → (4,Y-(4-X)) Verter agua del jarro de 3 litros en el jarro
de 4 litros hasta que el jarro de 4 litros
esté lleno.
8. (X,Y | X+Y≥3 y X>0) → (X-(3-Y),3) Verter agua del jarro de 4 litros en el jarro
de 3 litros hasta que el jarro de 3 litros
esté lleno.

63
9. (X,Y | X+Y≤4 ∧ Y>0) → (X+Y,0) Verter todo el contenido del jarro de 3 litros
en el jarro de 4 litros.
10. (X,Y | X+Y≤3 ∧ X>0) → (0,X+Y) Verter todo el contenido del jarro de 4 litros
en el jarro de 3 litros.

En la práctica, las reglas 3 y 4 deben omitirse, pues aunque representan acciones


que están permitidas en el dominio del problema, no tiene sentido aplicarlas, ya
que no nos acercan a la solución del mismo porque D no se puede medir.

Por supuesto, para resolver este problema de los jarros de agua, es necesario
implementar un mecanismo que seleccione la regla cuya parte izquierda
concuerde con el estado actual, genere los nuevos estados y así sucesivamente,
hasta alcanzar el estado final. Estos métodos los estudiaremos en las próximas
secciones.

En [Bra86] pueden verse las representaciones gráficas de los espacios de estado


para los problemas del mundo de bloques y del rompecabezas de 8 piezas.

Un espacio de estado es, justamente, otro formalismo para representar el


conocimiento. Se representa, usualmente, mediante un árbol donde cada nodo
corresponde a un estado particular del problema y cada arco corresponde a un
operador de transición de estados. La solución del problema puede ser definida,
entonces, como una búsqueda de un camino entre el nodo correspondiente al
estado inicial y el nodo correspondiente al estado final o meta. La forma en que se
represente cada estado varía considerablemente de problema en problema. Por
ejemplo, en el ajedrez puede ser una matriz de 8x8 donde cada elemento tiene el
caracter que representa la pieza situada en esa posición; en el problema del
camino entre las ciudades podría ser una cadena con el nombre de la ciudad y en
el problema de los jarros de agua podemos usar dos enteros. Si el problema que
queremos resolver es más complicado, podemos usar una de las F.R.C.
estudiadas en el capítulo anterior para representar cada estado individual.

Conclusión: Para proporcionar una descripción formal de un problema es


necesario:
- Definir un espacio de estado que contenga todas las configuraciones
posibles de los objetos relevantes. Naturalmente es posible definir este
espacio sin enumerar explícitamente todos los espacios que contiene.
- Especificar uno o más estados de ese espacio que constituyan los estados
iniciales.
- Especificar uno o más estados que serían aceptados como soluciones al
problema (estados metas).
- Especificar un conjunto de reglas que describen las acciones disponibles
(operadores).

El problema entonces puede resolverse usando las reglas de combinación con


una estrategia de control adecuada (proceso de búsqueda), para movernos a
través del espacio del problema hasta encontrar un camino desde el estado inicial
hasta el estado meta.

64
Espacio de reducción de problema.

A diferencia del espacio de estado, en este espacio el nodo inicial o nodo raíz del
árbol representa el problema original que se desea resolver, los nodos finales o
metas constituyen problemas que pueden resolverse mediante una primitiva
simple y los restantes nodos representan subproblemas en que puede
descomponerse un determinado problema. Los arcos serían, entonces, operadores
que permiten descomponer un problema en un conjunto de subproblemas.
Existen dos tipos de nodos. Si sólo basta con resolver uno de los subproblemas en
que se descompuso un nodo dado para resolver éste, el nodo se denomina nodo O
(or). Si por el contrario, deben ser resueltos todos los subproblemas en que se
descompuso el nodo, éste se denomina nodo Y (and).

Este tipo de espacio de búsqueda se representa, usualmente, mediante un árbol


y/o, el cual no es más que un árbol que contiene nodos del tipo Y y nodos del tipo
O.

Una solución de un problema sería, entonces, un subárbol que comienza en el


nodo raíz y que contiene siempre una de las ramas de los nodos O y todas las
ramas de los nodos Y hasta llegar a uno o varios nodos terminales.

En este tipo de espacio es conveniente representar aquellos problemas que


pueden ser descompuestos fácilmente en subproblemas, como por ejemplo, la
generación de estructuras químicas, la integración simbólica y el razonamiento
lógico.

En la figura 3.1 se muestra un ejemplo sencillo de un problema representado


mediante un árbol y/o. El problema de adquirir un televisor puede
descomponerse en dos subproblemas: robarlo o tratar de adquirirlo mediante una
vía legal. Esta última puede descomponerse a su vez en dos subproblemas, los
cuales deben ser resueltos para poder resolver el problema original.

Adquirir un TV

Robar un TV Ganar dinero Comprar un TV

Fig. 3.1. Árbol y/o para el problema de adquirir un televisor.

Analicemos otro ejemplo. Supongamos que tenemos el mapa representado en la


figura 3.2, donde se muestra un conjunto de ciudades y los caminos existentes
entre ellas. Se desea encontrar un camino desde la ciudad 1 a la 11. Existen dos
vías para lograr este camino: una pasando por la ciudad 6 y la otra pasando por
la ciudad 7. Pero el subproblema de encontrar un camino por la primera vía

65
significa encontrar un camino entre las ciudades 1 y 6 y un camino entre las
ciudades 6 y 11. Lo mismo ocurre para el subproblema de encontrar el camino
por la segunda vía. Una porción del árbol y/o correspondiente a esta
descomposición del problema original puede verse en la figura 3.3.

2 3

4 5 6

7 8 9

10 11

Fig. 3.2. Un mapa compuesto de 11 ciudades.

Por otro lado, ¿cómo podríamos representar el problema de la integración


simbólica en un árbol y/o? Esto se realizaría, fácilmente, situando como nodo
inicial la integral que se desea resolver. Los operadores de reducción del problema
serían, por ejemplo, la regla de la integral de la suma y la integración por partes,
las cuales generarían los nodos intermedios del árbol y las reglas que permiten
calcular la integral de funciones primitivas, las cuales generarían los nodos
terminales. Estos nodos, a su vez, serían funciones que no contienen el símbolo
integral.

66
Camino 1-11

OR

Camino 1-11 vía 6 Camino 1-11 vía 7

AND AND

Camino 1-6 Camino 6-11 Camino 1-7 Camino 7-11

OR

Camino 6-11 vía 8 Camino 6-11 vía 9

AND AND

Camino 6-8 Camino 8-11 Camino 6-9 Camino 9-11 Nodos


Terminales

Fig. 3.3. Porción del árbol AND/OR correspondiente al problema de la fig. 3.2.

Árboles de juego.

Un caso especial de los árboles y/o son los árboles de juego, donde participan dos
jugadores. En un árbol de juego el nodo raíz representa la posición inicial del
juego y los nodos terminales representan posiciones del juego donde un jugador
gana, pierde o hace tablas. Los operadores son, precisamente, los movimientos
legales que pueden realizarse en dicho juego. Este tipo de árbol se caracteriza por
tener en el primer nivel a los nodos que se derivaron de movimientos del primer
jugador y en el segundo nivel, a los que se derivaron de movimientos del segundo
jugador y así sucesivamente se van alternando por niveles.

¿Por qué un árbol de juego es un árbol y/o? Desde el punto de vista de un


determinado jugador, los nodos que permiten generar sus movimientos son nodos
O, pues basta seleccionar sólo uno que conduzca a la victoria. Sin embargo, los
nodos que permiten generar los movimientos de su oponente son nodos Y, ya que
él debe analizarlos a todos para poder neutralizar a su oponente.

En la figura 3.4 se muestra una porción del árbol de juego del titafor. Este
espacio de búsqueda tiene 362 880 nodos. Estas magnitudes demuestran la
necesidad de utilizar métodos de búsqueda eficientes.

67
3.2.1.2. Representación del espacio de búsqueda.

Hemos representado a los tres tipos de espacios de búsqueda estudiados en la


sección anterior, en forma de árbol. Sin embargo, con frecuencia, el proceso de
generación de los nodos sucesores produce un mismo nodo más de una vez, lo
que implica que en la búsqueda del camino a la solución, éste se procese varias
veces. En este caso, es mejor representar el espacio de búsqueda mediante un
grafo orientado.

Estado inicial

Movs. Jugador A

x x x
x x x
x x x

Movs. Jugador B

o o o
x x x o x x o x x x
o o o

Fig. 3.4. Porción del árbol de juego del titafor.

Una forma simple de realizar una estrategia de búsqueda es atravesando un árbol.


Se expande cada nodo del árbol mediante las reglas de producción para generar un
conjunto de nodos sucesores, cada uno de los cuales puede expandirse a su vez,
continuando hasta que se encuentra un nodo que representa la solución. Sin
embargo, con frecuencia, este proceso genera el mismo nodo formando parte de
diversos caminos y por tanto, es procesado más de una vez. En este caso es mejor
explorar un grafo orientado.

En el problema de los jarros de agua es fácil ver cómo un mismo nodo es


generado más de una vez (figura 3.5). El grafo correspondiente a este espacio de
búsqueda puede verse en la figura 3.6. Note cómo usando un grafo no se necesita
repetir los nodos.

68
(0,0)

(4,0) (0,3)

(4,3) ( 0, 0 ) (1,3) (4,3) (0,0) (3,0)

Fig. 3.5. Porción del espacio de búsqueda del problema de los jarros de agua
representado en forma de árbol.

(0,0)

(4,0) (0,3)

(1,3) (4,3) (3,0)

Fig. 3.6. Porción del espacio de búsqueda del problema de los jarros de agua
representado en forma de grafo.

En la creación del grafo, antes de incorporar un nodo N, deben realizarse los


siguientes pasos:
1) Examinar el conjunto de nodos que se ha creado hasta ahora para ver si ya
existe el nuevo nodo.
2) Si no existe añadirlo al grafo como si fuera un árbol.
3) Si ya existe, entonces realizar las siguientes acciones:
a) Hacer que el nodo que se está expandiendo apunte al nodo ya existente,
que corresponda a su sucesor en vez de uno nuevo. El nuevo puede
simplemente descartarse.
b) Si se está registrando el mejor camino a cada nodo, comprobar si el
nuevo camino es mejor o peor que el antiguo. Si es peor no hacer nada.
Si es mejor grabar el nuevo camino como el correcto para llegar al nodo
y propagar el cambio correspondiente por los nodos sucesivos que sean
necesarios.

El buscar en un grafo reduce el esfuerzo que se invierte en explorar un camino


varias veces, pero requiere un esfuerzo adicional para comprobar si el nodo ya se
ha generado. El hecho de si este esfuerzo es justificado depende del problema
concreto. Un problema que se puede presentar y que hay que tener en cuenta es
la presencia de ciclos en el grafo.

69
Hemos descrito la búsqueda como el proceso de recorrer un árbol o un grafo,
donde cada nodo representa un punto en el espacio del problema. Pero, ¿cómo
representaremos cada nodo individual? Por ejemplo, en el ajedrez puede ser una
matriz en la que cada elemento tenga el carácter que representa la pieza en esa
posición y en el problema de los jarros de agua podemos usar dos enteros. Si el
problema es más complicado usaremos una FRC estudiada en el tema anterior.

3.2.2. Segundo paso: análisis del problema.

Para escoger la técnica de I.A. más apropiada para resolver un problema es


necesario analizar el mismo teniendo en cuenta los siguientes aspectos:
- ¿Se puede descomponer el problema en un conjunto de subproblemas
independientes más pequeños o más fáciles?
- ¿Es posible en la solución del problema ignorar o deshacer pasos mal
hechos?
- ¿Es predecible el universo del problema?
- ¿Se desea una solución cualquiera o la mejor solución al problema?
- ¿Es consistente el conocimiento disponible para resolver el problema?
- ¿Cuál es el papel del conocimiento?
- ¿Se requiere la interacción con una persona?

En las próximas secciones explicaremos cada uno de estos aspectos.

3.2.2.1. ¿Se puede descomponer el problema?.

Los problemas que se pueden descomponer en un conjunto de subproblemas se


pueden solucionar usando la técnica de divide y vencerás (divide and conquer).
Sin embargo, estas técnicas generalmente no pueden usarse en aquellos
problemas no descomponibles, aunque a veces es posible usarlas para generar
una solución aproximada y, entonces, arreglarla para reparar los errores causa-
dos por las interacciones existentes entre los subproblemas.

Un ejemplo de problema es la integración simbólica.


Un ejemplo de problema no descomponible es el mundo de bloques.

3.2.2.2. ¿Es posible ignorar o deshacer pasos para la solución?.

En relación con este aspecto existen tres clases de problemas:

1. Ignorables: En ellos los pasos dados incorrectamente para la solución del


problema pueden ignorarse. Un ejemplo es la demostración de un teorema
matemático. Supongamos que comenzamos demostrando un lema que
pensamos es útil, pero después nos damos cuenta que no era de ninguna
ayuda. Cualquier regla que pudo haberse aplicado, todavía puede aplicarse.
Podemos simplemente ignorar lo hecho y lo único que se ha perdido es el
esfuerzo.
2. Recuperables: En ellos los pasos dados incorrectamente para la solución del
problema pueden deshacerse. Un ejemplo es el rompecabezas de 8 piezas, el

70
cual consiste en que se tiene una bandeja en la que se colocan ocho
baldosas cuadradas. El noveno cuadrante sobrante queda sin cubrir. Cada
baldosa tiene un número sobre ella. Una baldosa que esté adyacente al
espacio en blanco puede deslizarse a dicho espacio. El juego consiste en,
dadas una posición inicial y una posición final (usualmente las baldosas en
orden consecutivo con el espacio en blanco en el centro), transformar la
posición inicial en la final, desplazando las baldosas. Al intentar resolver
este problema, podemos realizar un movimiento tonto. Los errores cometidos
pueden enmendarse, retrocediendo para deshacer cada paso incorrecto.
Lógicamente hace falta memorizar el orden de los pasos realizados para
poder corregirlos. Note que esto no fue necesario en el caso anterior.
3. Irrecuperables: En ellos los pasos dados incorrectamente para la solución
del problema no pueden deshacerse. Un ejemplo es el juego del ajedrez. Si se
realiza un movimiento estúpido, no se puede ignorar ni retroceder al
principio de la partida. Lo único que puede hacerse es tratar de realizar la
mejor jugada a partir de la situación actual. Necesitan aplicar mucho
esfuerzo en la toma de decisiones. Usualmente se analiza por adelantado
una secuencia de pasos antes de dar el primero.

3.2.2.3. ¿Es predecible el universo del problema?.

En los problemas donde es posible predecir qué ocurrirá (problemas de resultados


ciertos), pueden usarse las técnicas de planificación, las cuales permiten generar
una secuencia de operadores que conducen con certeza a una solución. Sin
embargo, existen problemas donde esto no es posible. En ellos las técnicas de
planificación generan como máximo una secuencia de operadores que conducen
a una solución con una buena probabilidad.

Ejemplo de problema de resultados ciertos: en el del rompecabezas de 8 piezas,


cada vez que se hace un movimiento sabemos exactamente qué pasará, por lo que
se puede planificar una secuencia completa de movimientos y saber de antemano
el resultado.

Ejemplo de problema de resultados inciertos: en el juego Bridge (la brisca), para


decidir qué carta jugar no podemos planificar la partida completa, pues no
sabemos qué cartas tienen los restantes jugadores ni qué jugadas ellos harán. Lo
mejor que se puede hacer es investigar distintos planes y evaluar las
probabilidades de llegar a una buena puntuación, seleccionando la mayor.

Uno de los tipos de problemas más difíciles son los irrecuperables de resultados
inciertos. Ej: el Bridge.

3.2.2.4. ¿Se desea una solución cualquiera o la mejor solución al problema?.

Los problemas en los que se desea encontrar el mejor camino a la solución son
más difíciles de resolver que aquellos donde basta encontrar una solución
cualquiera, pues mientras los primeros requieren búsqueda exhaustiva, los
segundos pueden resolverse eficientemente usando técnicas heurísticas. Un

71
ejemplo de problema donde basta encontrar una solución se expone a
continuación.

Supongamos que se tiene el siguiente conjunto de hechos:


1. Marcos era un hombre.
2. Marcos era pompeyano.
3. Marcos nació en el año 40 d.C.
4. Todos los hombres son mortales.
5. Todos los pompeyanos murieron en la erupción del volcán en el año 79
d.C.
6. Ningún mortal tiene más de 150 años.
7. Hoy estamos en el año 1995 d.C.

y se desea responder a la pregunta ¿está vivo Marcos?. Representándolos en


lógica de predicados y realizando inferencias se llega fácilmente a una respuesta.
Note que en este caso existen dos caminos para llegar a la respuesta de que
Marcos no está vivo. Uno de ellos se obtiene utilizando los hechos 1,3,4,6 y 7 y el
otro, utilizando 2,5 y 7. No importa cuál de ellos se tome, lo que importa es la
respuesta a la pregunta.

Un ejemplo donde se desea encontrar la mejor solución es el problema del


vendedor ambulante, también conocido como el agente viajero, el cual consiste en
que un vendedor tiene una lista de ciudades, cada una de las cuales debe ser
visitada solamente una vez. Existen carreteras directas entre cada par de
ciudades de la lista. Se debe encontrar el camino más corto que debería seguir el
vendedor para visitar todas las ciudades, comenzando por una cualquiera y
retornando a ella misma. En este caso, es necesario analizar todos los caminos
posibles para tomar el mejor de ellos.

3.2.2.5. ¿Es consistente el conocimiento disponible para resolver el


problema?.

Hay problemas donde el conocimiento que se usa es completamente consistente.


Un ejemplo de ellos es: dado los siguientes axiomas de un grupo multiplicativo,
1. X⋅Y está definido ∀X,Y elementos del grupo.
2. (X=Y ∧ Y=Z) ⇒ X=Z
3. X=X
4. (X⋅Y)⋅Z = X⋅(Y⋅Z)
5. I⋅X=X ∀X, donde I es el elemento identidad del grupo.
6. X-1⋅X = I, donde X-1 es el inverso de X.
7. X=Y ⇒ Z⋅X=Z⋅Y
8. X=Y ⇒ X⋅Z=Y⋅Z
se desea demostrar que ∀X X⋅I=X.

Sin embargo, existen problemas que tienen inconsistencias. Un ejemplo es el


problema de la diana, el cual consiste en que un hombre está de pie a 50m de
una diana y quiere dar en el blanco con una pistola que dispara a una velocidad
de 500 m/s. ¿A qué distancia debe apuntar por encima del blanco?.

72
Razonemos: la bala tarda 0.1s en alcanzar el blanco, suponiendo que viaja en
línea recta. La bala cae a una distancia d:
d = 1/2gt2 = 1/2(9.8)(0.1)2 = 0.049 m = 4.9 cm.
Si el hombre apunta 4.9 cm por encima del blanco daría en la diana. Pero se ha
supuesto que la bala viaja en línea recta, lo que entra en conflicto conque viaja en
una parábola.

3.2.2.6. ¿Cuál es el papel del conocimiento?.

Existen problemas donde se necesita conocimiento sólo para restringir la


búsqueda. Por ejemplo, en el problema del ajedrez, suponiendo potencia de
computación ilimitada, se necesita de poca cantidad de conocimiento: sólo reglas
para describir los movimientos legales del juego y un proceso de búsqueda
adecuado. Usar conocimiento adicional sobre táctica y buena estrategia, ayudaría
a restringir la búsqueda de la solución y acelerar la ejecución del programa. Sin
embargo, hay problemas que necesitan gran cantidad de conocimiento tan sólo
para reconocer una solución. Ejemplo: explorar los periódicos de Estados Unidos
para decidir quién está apoyando a los demócratas o a los republicanos en una
elección próxima. El programa tendría que saber cosas como los nombres de los
candidatos de cada partido, el hecho de que si quiere bajar los impuestos apoya a
los republicanos, el hecho de que si quiere educación para las minorías apoya a
los demócratas, etc. Mucho conocimiento, a veces, se usa para acotar la
búsqueda, pero otras para reconocer una solución.

3.2.2.7. ¿Requiere la interacción con una persona?.

Teniendo en cuenta este aspecto podemos distinguir dos tipos de problemas:

- Solitario, en el cual se le da a la computadora una descripción del problema y


ella produce una respuesta sin comunicación intermedia y sin petición de
una explicación de su razonamiento. Ejemplo: para demostrar un teorema
matemático usando resolución, lo único que se desea es saber si existe una.
- Conversacional, para proporcionar asistencia adicional a la computadora y/o
proporcionar información adicional al usuario. Ejemplo: en problemas como
el diagnóstico médico, el programa debe ser capaz de explicar su
razonamiento, pues si no, no será aceptado por los médicos.

3.2.2.8. ¿La solución es un estado o una ruta?.

Ejemplo de un estado: El presidente del banco comió un plato de ensalada de


pasta.
Si se aíslan las componentes existen varias interpretaciones de: banco, comió un
plato, ensalada de pasta (contiene pasta pero “comida de perros” no contiene
perros). Se realiza una búsqueda de todas la interpretaciones hasta encontrar la
de esta oración. Solo interesa esta interpretación.

Ejemplo de ruta: Las jarras de agua. No me interesa el estado final sino la


consecuencia de operaciones.

73
3.2.3. Tercer paso: Aislar y representar el conocimiento necesario para
resolver el problema.

Ya en las secciones anteriores se estudió este tema.

3.2.3. Cuarto paso: Aplicar la mejor técnica de I.A. para el problema


particular.

Para poder escoger la mejor técnica de I.A. a aplicar en el proceso de resolución


de un problema particular es necesario estudiar, primeramente, las distintas
técnicas de búsqueda que existen. A esto, precisamente, nos dedicaremos en las
siguientes secciones de este capítulo.

3.3. Métodos de búsqueda a ciegas.

La búsqueda a ciegas es una colección de procedimientos usados para buscar en


un espacio de estados de manera exhaustiva pero ciega. Estos procedimientos se
consideran métodos débiles, pues imponen restricciones mínimas a la búsqueda,
en general son técnicas de solución de problemas de propósito general y pueden
describirse independientemente de cualquiera sea el dominio del problema. Estos
métodos usan solamente la información estructural y no hacen ninguna distinción
cualitativa entre los nodos, respecto a su posibilidad de encontrarse sobre el
camino deseado. En consecuencia, para los problemas con un extenso espacio de
estados, la cantidad de alternativas que deben explorarse es tan grande que hace
que su uso sea computacionalmente imposible. El número de nodos a explorar
crece, en general, exponencialmente con la longitud del camino que representa la
solución del problema. Esto genera una explosión combinatoria que estos métodos
son incapaces de superar. No obstante, continúan formando el núcleo de la
mayoría de los sistemas de I.A. Las cuatro técnicas de búsqueda a ciegas más
usadas son: primero a lo ancho, primero en profundidad, encadenamiemto adelante
(forward chaining) y encadenamiento hacia atrás (backward chaining). Ellas pueden
ser combinadas, por ejemplo, el encadenamiento hacia adelante puede ser primero
en profundidad o primero a lo ancho.

3.3.1. Algoritmo del museo británico.

Este procedimiento demuestra cuán ineficiente puede resultar un algoritmo de


búsqueda. Consiste en colocar a un mono delante de una máquina de escribir y
que presionando aleatoriamente las teclas genere todos los trabajos de
Shakespeare existentes en el museo. Para generar una frase de 18 caracteres
tendría una probabilidad de 1 en 2718. De esta forma, el procedimiento consiste
en generar todas las soluciones posibles y comprobar cuál es la correcta. Con un
tiempo suficiente logra encontrar la solución optimal, sólo que es intratable
computacionalmente.

74
3.3.2. Búsqueda primero a lo ancho.

Una búsqueda primero a lo ancho (breadth-first) genera y explora primero todos


los sucesores del nodo raíz. Si no se encuentra la meta, pasa a los sucesores del
segundo nivel y así sucesivamente por niveles. Suponiendo que el objetivo a
alcanzar es el nodo 7, el recorrido primero a lo ancho del espacio de búsqueda
que se muestra en la figura 3.7, es 1-2-3-4-5-6-7. Este método simboliza a un
explorador bastante conservador.

2 3

4 5 6 7

8 9 10 11 12

Fig. 3.7. Un ejemplo de espacio de búsqueda.

Si el número máximo de hijos (o ramas) de un nodo es b y la profundidad de la


solución es d, entonces el número de nodos en el nivel d es bd y la cantidad de
tiempo usada en la búsqueda es en el caso peor:

1 + b + b2 + ... + bd,

Para grandes valores de d, puede ser aproximada por bd. Es por esto que la
complejidad temporal de este método de búsqueda es O(bd), lo cual es una función
exponencial de d.

Este método tiene como ventaja que siempre encuentra el camino más corto a la
solución, si ésta existe, aún en el caso de que el espacio de búsqueda sea infinito,
por eso la búsqueda primero a lo ancho siempre encuentra una solución óptima
para esa medida.

El método es efectivo cuando el factor de ramificación, o sea, el número promedio


de hijos de un nodo, es pequeño, pues entonces la cantidad de nodos por niveles
será pequeña y es mejor explorar un nivel antes de pasar al siguiente.

Sin embargo, tiene las siguientes desventajas:


- Necesita mucha memoria. Como cada nivel del árbol tiene que ser
almacenado completamente para poder generar el próximo nivel y la
cantidad de memoria es proporcional al número de nodos almacenados, su
complejidad espacial es también O(bd).
- Requiere mucho trabajo, especialmente si el camino más corto a la solución
es muy largo, puesto que el número de nodos que necesita examinar se
incrementa exponencialmente con la longitud del camino.

75
- Los operadores irrelevantes o redundantes incrementarán grandemente el
número de nodos que deben explorarse.

Además, este método puede llevar a una búsqueda exhaustiva. Ella es


particularmente inapropiada en situaciones donde hay muchos caminos que
conducen a soluciones, pero cada uno de ellos es muy largo. En tales situaciones es
muy probable que sea más rápida la búsqueda primero en profundidad.

Una forma de implementación de este método es usar un conjunto de caminos


candidatos:
- Si el primer camino está encabezado por un nodo objetivo, ésta es la solución
del problema.
- Si no,
a) Remover el primer camino del conjunto de caminos candidatos, generar el
conjunto de todas las posibles extensiones un nivel más de este camino y
añadir las mismas al final del conjunto.
b) Repetir el proceso.

Tomando el ejemplo mostrado en la figura 3.7 este proceso se desarrollaría como


sigue:
a) Conjunto de caminos candidatos (CCC) inicial
[[1]]
b) Generando las extensiones de [1]
[[2,1] , [3,1]]
c) Al remover el primer camino candidato y generar sus extensiones:
[[4,2,1] , [5,2,1]] se obtiene el nuevo CCC:
[[3,1] , [4,2,1] , [5,2,1]]
d) Removiendo y generando nuevamente las extensiones [[6,3,1] , [7,3,1]] se
obtiene el CCC:
[[4,2,1] , [5,2,1] , [6,3,1] , [7,3,1]]
e) Así sucesivamente son obtenidos los siguientes conjuntos hasta llegar a:
[[7,3,1] , [8,4,2,1] , [9,4,2,1] , [10,5,2,1], [11,6,3,1]]
En este caso el camino inicial tiene como cabeza el nodo objetivo y el proceso
termina en esta solución.

Como en los caminos del CCC hay mucha información repetida se puede emplear
un árbol. El árbol equivalente al CCC del paso (d) es:
t(1, [t(2, [h(4), h(5)] ), t(3, [h(6), h(7)] )] )

El procedimiento anterior puede escribirse en el lenguaje PROLOG como sigue:


busq_ancho([[Nodo|Resto]|_],[Nodo|Resto]):- meta(Nodo).
busq_ancho([[Nodo|Resto]|Otros],Solucion):-
bagof([M,Nodo|Resto],(sucesor(Nodo,M), not miembro(M,[Nodo|Resto])),
NuevoCamino),
append(Otros,NuevoCamino,Camino1), !,
busq_ancho(Camino1,Solucion).

En caso de que Nodo no tenga sucesor, entonces


busq_ancho([[Nodo|Resto]|Otros],Solucion):- busq_ancho(Otros,Solucion).

76
Aquí meta/1 es un hecho que indica el nodo objetivo, miembro/2 es un predicado
que determina si un elemento es miembro de una lista, sucesor/2 indica el
sucesor de un nodo dado y append/3 concatena dos listas.

En [IPN88] puede verse otra implementación de este algoritmo en PROLOG y una


en LISP.

3.3.3. Búsqueda primero en profundidad.

La búsqueda primero en profundidad (depth-first) explora, primeramente, el nodo


raíz y luego genera el sucesor de éste ubicado en la rama más a la izquierda. Si
este nodo es el objetivo, entonces hemos encontrado el camino. Si no, se continúa
extendiendo este camino tomando siempre el primer sucesor. Si el nodo no tiene
más sucesores, se pasa al siguiente sucesor de su predecesor, o sea, se retrocede
al nivel anterior para tomar el otro sucesor y así sucesivamente, hasta alcanzar el
objetivo o hasta que se realice un corte a alguna profundidad determinada. El
recorrido primero en profundidad del espacio de búsqueda mostrado en la figura
3.7 como el objetivo se alcanza en el nodo 7 es: 1-2-4-8-9-5-10-3-6-11-7. Este
método simboliza a un explorador que toma riesgos.

La ventaja de este método está en la eficiencia que se alcanza en el uso de la


memoria. Si la longitud máxima de una rama del árbol es d nodos, como sólo se
necesita almacenar el camino actual, entonces la complejidad espacial del
algoritmo es O(d). En la práctica la búsqueda por este método se limita por el
tiempo y no por el espacio.

La desventaja del método es que si existen ramas infinitas, puede no encontrar la


solución al problema, aún teniéndola. Es por esto que, en ocasiones, se requiere
que se defina un corte a una profundidad arbitraria para evitar, lo más posible,
caer en caminos o lazos infinitos. Si la profundidad de corte seleccionada c es
menor que la profundidad de la solución d, el algoritmo terminará sin encontrar
una solución, mientras que si c > d, se paga un precio alto, O(bc), en términos del
tiempo de ejecución, donde b es el número máximo de hijos (o ramas). Si no
existe el peligro de que el espacio de búsqueda tenga caminos infinitos pero sí
pueden aparecer lazos (el espacio de búsqueda no es un árbol sino un grafo) una
alternativa al corte es incluir un detector de ciclos (chequeo de nodos repetidos).

El procedimiento anterior puede escribirse en el lenguaje PROLOG como sigue:


busq_prof(Nodo,[Nodo]):- meta(Nodo).
busq_prof(Nodo,[Nodo|Solucion]):- sucesor(Nodo,Nodo1),
busq_prof(Nodo1,Solucion).

Aquí meta/1, nuevamente, es un hecho que indica el nodo objetivo y sucesor/2


indica el sucesor de un nodo dado.

En [Bra86] e [IPN88] pueden verse otras implementaciones de este algoritmo en


PROLOG, usando corte de profundidad y chequeo de existencia de ciclos. En
[IPN88] puede verse además una realizada en LISP.

77
La búsqueda primero en profundidad es mejor cuando el nodo objetivo está
situado en la porción inferior izquierda del árbol de búsqueda, mientras que la
búsqueda primero a lo ancho es mejor cuando el nodo objetivo está situado en la
porción superior derecha de dicho árbol, según se muestra en la figura 3.8.

Algunos autores recomiendan seleccionar entre la búsqueda primero en


profundidad (BPP) y la primero a lo ancho, la primera de ellas, a no ser que exista
alguna información que aconseje lo contrario.

Una variante del método de búsqueda primero en profundidad es la búsqueda


iterativa primero en profundidad (BIPP). Esta consiste en realizar una búsqueda
primero en profundidad con corte 1, luego otra para el corte 2 y así
sucesivamente, incrementando en uno la profundidad de corte, hasta encontrar
la solución. Como este método nunca genera un nodo hasta que todos los nodos
de los niveles anteriores han sido generados y examinados, se garantiza encontrar
la solución óptima. Además, como en cada momento se está ejecutando una
búsqueda primero en profundidad, la complejidad espacial es O(d). Aunque
parezca lo contrario, se puede demostrar que la complejidad temporal es O(bd), al
igual que en la búsqueda primero a lo ancho. La razón de esto es que como el
número de nodos, en un nivel dado del árbol, crece exponencialmente con la
longitud del camino o profundidad, casi todo el tiempo se gasta en el nivel más
profundo. El recorrido de este método para la figura 3.7 es 1-2-3-1-2-4-5-3-6-7.

En la figura 3.8 se compara el orden de generación de los nodos del árbol por los
tres métodos analizados.

* * *

1* *2 1* *4 1,3* *2,6

3* 4* *5 *6 2* 3* *5 *6 4* 5* *7 *8

Búsqueda primero Búsqueda primero Búsqueda iterativa


a lo ancho en profundidad primero en profundidad

Figura 3.8. Generación de nodos por tres métodos de búsqueda.

78
3.3.4. Búsqueda en árboles AND/OR.

Los algoritmos para búsqueda primero a lo ancho y primero en profundidad en


árboles AND/OR son similares a los anteriormente estudiados, sólo se diferencian
en la verificación de las condiciones de terminación. Cada vez que se genera un
nodo o un grupo de nodos, el algoritmo tiene que chequear si se satisfacen las
condiciones de terminación de acuerdo al tipo de nodo, ya sea Y u O. La siguiente
figura muestra una comparación de los dos métodos de búsqueda

Fácil para ambas Mejor: a lo ancho

Mejor: en Difícil para


profundidad ambas

3.3.5. Búsqueda en sistemas de producción.

Cuando el conocimiento se encuentra representado utilizando reglas de producción


es usual utilizar dos métodos de búsqueda. La búsqueda se puede hacer desde un
estado inicial a un estado objetivo, o sea, desde las evidencias a las conclusiones,
en este caso se denomina búsqueda con encadenamiento hacia adelante (forward
chaining) o enfoque guiado por datos (datadriven). También podrá realizarse en
dirección contraria, comenzando desde el estado objetivo aplicando movimientos
inversos hasta alcanzar un estado inicial, o sea, el razonamiento se realiza desde
una hipótesis (objetivo) hasta las evidencias necesarias para refutar o confirmar esa
hipótesis, y en cada movimiento inverso se puede generar un subobjetivo, a este
método se le llama Búsqueda con encadenamiento hacia atrás (backward chaining) o
enfoque guiado por objetivos (goaldriven).

Aunque no hay una diferencia formal entre un sistema de producción que trabaja
sobre un problema en una dirección hacia adelante y uno que trabaja en dirección
contraria, es conveniente hacer esta distinción explícita. Cuando un problema tiene
intuitivamente claro los estados y los objetivos, y además se decide emplear las
descripciones de estos estados como la base de datos del sistema de producción,
entonces se dice que el sistema de producción es con encadenamiento hacia
adelante. En este caso las reglas se aplican a la descripción de estados para
producir nuevas descripciones de estado. Estas reglas son llamadas F-reglas.

Si se decide usar las descripciones de los objetivos como base de datos, y las
reglas son aplicadas a las descripciones de los objetivos para producir descripciones

79
de subobjetivos, el sistema de producción es con encadenamiento hacia atrás y las
reglas se denominan B-reglas.

Los pares de términos Forward - backward, Datadriven - goaldriven, y Bottom up -


top down expresan esencialmente la misma distinción, ellos sólo difieren
generalmente por el área en que se usan. El primer par es más usado en los
sistemas basados en reglas, el segundo por lo general en la solución de problemas y
el último par, en tareas de análisis sintáctico (parsing). En estos momentos, ellos
son virtualmente intercambiables. Los primeros autores en usar los términos
bottom up y top down fueron Cheatham y Sattley en el trabajo "Syntax Directed
Compiling" (1964), y Griffiths y Detrick en el trabajo "On the relative efficience of
context-free grammar recognizes" (1965). Newel, Shaw y Simons usaron los
términos forward y backward en "Empirical Explorations with the logic theory
machine" (1957). En el trabajo "Element of Psychology" (1958) se usó por primera
vez el término goal-directed. Por último, data-driven fue empleado por Bobrow y
Norman en "Some Principles of Memory Schemata".

Estudiemos con profundidad estos métodos de búsqueda, así como una


combinación de ambos.

3.3.5.1. Búsqueda con encadenamiento hacia adelante (enfoque guiado por


datos).

En la búsqueda con encadenamiento hacia delante se comienza a construir el


árbol situando como raíz al estado inicial. El siguiente nivel del árbol se genera
encontrando todas las reglas cuyas partes izquierdas concuerden con el nodo raíz
y usando sus partes derechas para crear los nuevos estados. De esta manera el
proceso continúa hasta generar un estado que concuerde con el estado meta.

En este método las reglas son sólo aplicables si su parte condición es satisfecha
por la B.D. El siguiente procedimiento da la idea algorítmica de un módulo de
aplicación de reglas sencillo que está basado en este enfoque.

Procedimiento generar:
1) Identificar el conjunto S de reglas aplicables.
2) Mientras S no sea vacío,
a) Seleccionar una regla R de S.
b) Aplicar R, generando los nuevos estados y añadiéndolos a la B.D.
c) Si se generó el estado objetivo entonces TERMINAR y EXITO.
d) Si no, llamar nuevamente al procedimiento generar.
e) Eliminar R de S y anular el efecto de aplicar R.

Este proceso de encadenamiento hacia delante tiene un carácter no


determinístico, pues el ordenamiento de las reglas aplicadas no está
explícitamente definido en el caso de que el conjunto de reglas aplicables esté
formado por más de una regla. Ellas forman, precisamente, el llamado conjunto
conflicto. En el capítulo anterior mencionamos algunas técnicas de resolución de
conflictos en los sistemas de producción.

80
Este procedimiento genera nuevos hechos a partir de la BD usando las reglas y
luego los añade.

Base de reglas
R1: Si X es divisible por 12, entonces X es divisible por 6.
R2: Si X es divisible por 20, entonces X es divisible por 10.
R3: Si X es divisible por 6, entonces X es divisible por 2.
R4: Si X es divisible por 10, entonces X es divisible por 5.

Supóngase que sabemos que algún número N es divisible por 12 y por 20 y que el
problema es determinar si N es divisible por 5. Comenzamos entrando esos datos a
la BD:

Base de Datos inicial


N es divisible por 12.
N es divisible por 20.

Analicemos el ejemplo. Ya tenemos la base de reglas y la B.D. inicial y el


problema a resolver. Comencemos a aplicar el procedimiento generar, suponiendo
que el sistema siempre escoge la primera regla que aparece:
S={R1,R2}. Se selecciona R1.

Ahora la B.D. es: N es divisible por 12.


N es divisible por 20.
N es divisible por 6.

Ningún hecho concuerda con el estado objetivo. Se llama de nuevo a generar.


S1={R2,R3}.

R1 no está incluido en S1 porque su aplicación no cambiaría el estado en curso de


la B.D. Se aplica ahora R2 y la B.D. es:
N es divisible por 12.
N es divisible por 20.
N es divisible por 6.
N es divisible por 10.

Nuevamente llamamos a generar.


S2={R3,R4}.

R1 y R2 no están en S2 porque no son aplicables. Se selecciona ahora a R3 y la


B.D. es:
N es divisible por 12.
N es divisible por 20.
N es divisible por 6.
N es divisible por 10.
N es divisible por 2.

Al llamar nuevamente a generar,


S3={R4}.

81
Se aplica R4 y se llega a la solución del problema.

Un listado de cómo se consiguió esta solución da el orden en que se utilizaron las


reglas: R1, R2, R3, R4. El procedimiento finaliza realizando lo siguiente:

- Anula el efecto de R4, es decir, elimina N es divisible por 5 de la B.D.


- Elimina R3 de S2.
- Anula el efecto de R3, eliminando N es divisible por 2 de la B.D.
- Se continúa en el ciclo del paso 2).
- Se selecciona R4 de S2 y se aplica.

Se obtiene otra solución: R1, R2, R4. El proceso continúa hasta que se hayan
establecido todas las maneras de poder conseguir la solución. El espacio de
búsqueda para este problema está representado en la figura 3.9. Note cómo en
este espacio cada operador de transición de estados es precisamente una regla de
producción.
Estado inicial

R1 R2

R2 R3 R1 R4
E → éxito
E
R3 R4 R2 R3 R4

E E
R4 R4 R4
E
E E
Fig. 3.9. Espacio de búsqueda del ejemplo del encadenamiento hacia delante.

En algunas aplicaciones el listado de las reglas podría utilizarse como plan para
conseguir un objetivo en un área de aplicación que fuera modelada por el sistema
de producción. En tal caso, podría ser útil identificar el camino más corto desde el
estado inicial al objetivo.

Ventajas del enfoque guiado por datos


- Simplicidad
- Puede utilizarse para proporcionar todas las soluciones a un problema dado.

Desventajas
El comportamiento del sistema, al intentar solucionar un problema, puede ser
ineficaz y puede parecer también desatinado porque algunas de las reglas
ejecutadas podrían no estar relacionadas con el problema en cuestión. En el
ejemplo estudiado las reglas 1 y 3 no nos acercan a la solución del problema.

82
3.3.5.2. Búsqueda con encadenamiento hacia atrás (enfoque guiado por
objetivos).

En el encadenamiento hacia atrás se comienza a construir el árbol situando como


raíz al estado objetivo. El siguiente nivel del árbol se genera encontrando todas
las reglas cuyas partes derechas concuerden con el nodo raíz y usando las partes
izquierdas para crear los nuevos estados. Todas estas son las reglas que
generarían el estado que queremos si pudiésemos aplicarlas. Este proceso
continúa hasta generar un estado que concuerde con el estado inicial.

En este enfoque el sistema centra su atención, únicamente, en las reglas que son
relevantes para el problema en cuestión. En él el usuario comienza especificando
un objetivo mediante la declaración de una expresión E cuyo valor de verdad hay
que determinar. La siguiente función da la idea algorítmica de un módulo simple
de aplicación de reglas que utiliza este enfoque.

función validar: Tiene como entrada a una expresión X.


1) Resultado:= falso
2) Identificar el conjunto S de reglas aplicables que tengan a X en su parte
derecha.
3) Si S es vacío, entonces pedir al usuario que añada reglas a la base.
4) Mientras Resultado sea falso y S no sea vacío,
a) Seleccionar y eliminar una regla R de S.
b) C:= parte izquierda de R.
c) Si C es verdadero en la B.D., entonces Resultado:= verdadero.
d) Si C no es ni verdadero ni falso en la B.D., entonces
i) Llamar a validar pasándole como argumento a C.
ii) Si validar retornó verdadero, entonces Resultado:= verdadero.
5) RETORNAR VERDADERO.

Para ejecutar esta función, el usuario pregunta por el valor de verdad de validar
tomando como argumento la expresión E, donde E puede ser, por ejemplo, “N es
divisible por 5”. Lo primero que hace esta función es identificar todas las reglas
que tienen a E en el miembro derecho suponiendo que se hacen sustituciones
apropiadas. Si no existen tales reglas, se pide al usuario que proporcione alguna.
Si hay más de una regla, el sistema selecciona una, usando alguna estrategia de
resolución de conflictos. Cuando una regla es seleccionada, su parte condición C
es verificada con respecto a la B.D. Si C es verdadera en la B.D., entonces se
establece la verdad de E y el proceso puede terminar con éxito. Si C es falsa en la
B.D., entonces no se puede utilizar R para establecer la verdad de E y se
selecciona otra regla de S. Si C es desconocida (es decir no es verdadera ni falsa
en la B.D.), entonces se le considera como nuevo subobjetivo y se intenta
establecer su valor de verdad llamando recursivamente a validar con C como
argumento. Si validar(C) es verdadero, entonces la regla R es aplicable, se
establece la verdad de E y el proceso puede terminar con éxito. Si validar(C) es
falso, entonces se selecciona otra regla de S. Así, el proceso opera hacia atrás a
partir del objetivo, intentando alcanzar subobjetivos que puedan establecer dicho
objetivo por sí mismos.

83
Analicemos el ejemplo estudiado en la sección anterior, considerando las reglas y
la B.D. inicial utilizados en el ejemplo del tópico anterior. Supongamos
nuevamente que se quiere determinar si N es divisible por 5.

S={R4}, pues R4 es la única regla con X es divisible por 5 en el miembro


derecho.

Se genera entonces el subobjetivo N es divisible por 10 mediante la sustitución de


N por X en la parte condición de R4. Como N es divisible por 10 no es ni
verdadera ni falsa en la B.D. se llama a validar tomando como argumento la
expresión “N es divisible por 10”.

S1={R2}.

Se genera el subobjetivo N es divisible por 20 que es verdadero en la B.D. Así la


llamada inicial a validar termina con verdadero.

Los sistemas guiados por objetivos preguntan tanto al usuario como a la B.D. al
determinar la verdad de un subobjetivo. Así, el usuario no necesita entrar todos
los datos disponibles inicialmente, sino que puede esperar hasta que el sistema
pida datos.

Si se requieren todas las maneras de establecer E, se elimina de la condición del


ciclo en el paso 4) Resultado=falso y se deja sólo S no es vacío.

La función validar es simple, en el sentido de que supone que las partes


izquierdas de las reglas constan de declaraciones únicas, o sea, que no contienen
conectivas lógicas. Es necesario, entonces, extender esta función si se desea
trabajar con este método de búsqueda.

El encadenamiento hacia atrás tiene como ventaja que no solicita datos ni aplica
reglas que no estén relacionadas con el problema en cuestión.

Este enfoque es el método que se ha de elegir si se utiliza un sistema de producción


para comprobar una hipótesis concreta. Sin embargo, puede utilizarse para
comprobar cada hipótesis posible, proporcionando por tanto, todas las respuestas
que se pueden hacer utilizando el enfoque guiado por datos.

Nótese que en ambos métodos de búsqueda pueden usarse las mismas reglas,
tanto para razonar hacia delante desde el estado inicial como para razonar hacia
atrás desde el estado objetivo. Dependiendo de la topología del espacio del
problema, puede ser significativamente más eficiente la búsqueda en un sentido
que en otro. Tres factores influyen en esto:
- ¿Existen más estados iniciales posibles o más estados finales?. Preferiríamos
movernos desde un conjunto de estados lo más pequeño posible hacia el
conjunto de estados mayor, y por tanto, más fácil de encontrar. Un ejemplo
que ilustra esto es el siguiente: es más fácil conducir desde un lugar
desconocido a nuestra casa que desde ella a ese lugar desconocido. Existen
muchos más lugares que nos ayudan a llegar a nuestra casa de los que nos

84
pueden ayudar a llegar al lugar desconocido. Si podemos llegar a cualquiera
de ellos podemos llegar a casa fácilmente. Por tanto si nuestra posición de
partida es nuestra casa y nuestra meta el lugar desconocido, es mejor razo-
nar hacia atrás. Otro ejemplo es la integración simbólica, donde el estado
inicial es una fórmula que tiene el símbolo integral y el estado meta es un
conjunto de fórmulas que no tengan el símbolo integral. Luego empezamos
con un único estado inicial y un enorme número de estados metas. Es mejor
razonar hacia delante, usando las reglas de integración a partir de la
fórmula inicial, que empezar con una expresión arbitraria libre de integrales,
usando las reglas de diferenciación e intentar generar la integral inicial. En
los sistemas de diagnóstico hay pocas metas posibles, por lo que es mejor
usar el encadenamiento hacia atrás. Sin embargo, en el juego del ajedrez la
única alternativa posible es utilizar el encadenamiento hacia delante, pues el
número de hipótesis en este caso es virtualmente ilimitado.

- ¿En qué dirección el factor de ramificación, o sea, el promedio de nodos hijos


es mayor? Nos gustaría avanzar siempre en la dirección en que éste sea
menor. Un ejemplo es el problema de demostrar teoremas. Nuestros estados
iniciales son un pequeño conjunto de axiomas y los estados finales los
teoremas a demostrar. Ninguno de estos conjuntos es significativamente
más grande que el otro. Consideremos, entonces, el factor de ramificación. A
partir de un conjunto pequeño de axiomas podemos derivar un número muy
elevado de teoremas. Usando el otro enfoque de este elevado número de
teoremas debe regresarse a un pequeño número de axiomas. El factor de
ramificación es significativa-mente mayor si vamos hacia delante, desde los
axiomas hasta los teore-mas, que al revés. Por lo tanto, es mejor razonar
hacia atrás. De hecho, los matemáticos ya se han dado cuenta de esto. Uno
de los primeros programas de I.A., el Lógico Teórico (Newell 1963) usaba el
razonamiento hacia atrás para demostrar diversos teoremas del primer
capítulo de los Principia de Russell y Whitehead.

- ¿Se le pedirá al programa que justifique su proceso de razonamiento? Es


importante avanzar en la dirección que concuerde más con la forma en que
piensa el usuario. Por ejemplo, los doctores se niegan a aceptar el consejo de
un programa de diagnóstico que no pueda explicar su razonamiento. MYCIN,
un S.E. que diagnostica enfermedades infeccio-sas, razona hacia atrás para
determinar la causa de la enfermedad del paciente. Para ello usa reglas tales
como “si el organismo tiene el siguiente conjunto de características
determinadas por los resultados del laboratorio, entonces es probable que el
organismo sea X”. Al razonar hacia atrás puede responder a preguntas como
¿por qué debería realizar esa comprobación que acaba de pedir? y tendría
respuestas como “porque ayudaría a determinar si el organismo X está
presente”.

3.3.5.3. Búsqueda bidireccional.

La búsqueda bidireccional es una combinación de las dos búsquedas anteriores.


Consiste en realizar simultáneamente una búsqueda con encadenamiento hacia
adelante desde el estado inicial y una búsqueda con encadenamiento hacia atrás

85
desde el estado objetivo, hasta que ambas se encuentren en un estado común en
la frontera de ambas búsquedas. El camino a la solución se obtiene, entonces,
concatenando el camino desde el estado inicial con el inverso del camino desde el
estado objetivo. Para poder garantizar que ambas búsquedas se encuentren al
menos una de ellas debe seguir la estrategia de la búsqueda primero a lo ancho.
La utilización de este método exige, además, que los operadores del problema
sean invertibles.

Suponiendo que las comparaciones para identificar estados comunes pueden ser
hechas en un tiempo constante por nodo, la complejidad temporal es O(bd/2), ya
que cada búsqueda sólo necesita llegar hasta la mitad de la profundidad de la
solución. La complejidad espacial es también O(bd/2), pues una de las búsquedas
usa la estrategia de búsqueda primero a lo ancho. Recuerde que b es el máximo
número de hijos (o ramas) de los nodos y d, la profundidad de la solución.

Este método parece atrayente si el número de nodos de cada paso crece


exponencialmente con el número de pasos que se dan. Resultados empíricos
sugieren que para buscar a ciegas, esta estrategia es realmente efectiva.
Desafortunadamente, otros resultados sugieren que para una búsqueda
heurística lo es mucho menos, pues puede ocurrir que las búsquedas no se
encuentren y se necesite más trabajo que el que habría dado realizar una sola de
ellas. En un programa construido cuidadosamente pudiera emplearse con éxito.

3.3.6. Búsqueda por diferencias.

Se han estudiado estrategias hacia delante y hacia atrás. A veces son útiles
estrategias mixtas. Con ellas se resuelven, en 1er lugar, las partes principales y
luego se vuelve atrás a resolver los “pequeños” problemas que surgen al “pegar” los
trozos grandes.

La idea esencial de este método de búsqueda es que la acción a ejecutar se


selecciona sobre la base de examinar las diferencias entre lo que se tiene y lo que se
quiere, lo cual se puede representar por:

1) Examinar lo que se tiene. Estado actual:= Estado inicial.


2) Comparar el estado actual con el estado meta. Si no hay diferencias,
entonces TERMINAR y EXITO.
3) Determinar qué operador (u operadores) son posibles para reducir las
diferencias.
4) Aplicar sucesivamente los operadores encontrados hasta hallar uno que
sirva y generar el nuevo estado actual.
5) Ir al paso 2).

El proceso de análisis por reducción de diferencias se centra en la detección de


diferencias entre el estado actual y el estado meta. Una vez que se ha aislado una
diferencia, debe encontrarse un operador que pueda reducirla. Pero quizás el
operador no pueda aplicarse al estado actual. Por tanto, estableceremos un
subproblema que consiste en llegar a un estado en que pueda aplicarse dicho
operador y quizás este operador no produzca el estado meta deseado. Entonces

86
tenemos el segundo subproblema de llegar desde el estado producido por el
operador hasta el estado meta. Pero si la diferencia se ha escogido correctamente
y el operador es efectivo para reducirla, debería ser más fácil resolver los dos
subproblemas que el problema original. El proceso de reducción de diferencias
puede aplicárseles recursivamente. Para enfocar la atención del sistema en los
problemas mayores deben asignarse niveles de prioridad a las diferencias y
considerarse primero las de mayor prioridad.

Uno de los sistemas de solución de problemas, el desarrollado por Newel, Shaw y


Simon, se basa en este método. A este sistema se le llamó Solucionador General
de Problemas (GPS). El GPS usa una técnica para identificar alguna F-regla a
partir de una descripción de estado, S, y un objetivo, G. El proceso de identificación
primero intenta calcular una diferencia entre S y G. Este proceso de cálculo de
diferencias se resuelve por una función que tiene que ser escrita especialmente
para cada dominio de aplicación.

Las diferencias son usadas para seleccionar F-rules relevantes accediendo a una
"tabla de diferencias" en la cual las F-rules están asociadas con las diferencias. Las
F-rules asociadas con una diferencia son aquellas F-rules que son relevantes para
reducir esa diferencia. Es necesario definir una tabla de diferencias para cada
dominio de aplicación.

La esencia de este procedimiento es la siguiente: dadas las descripciones del


estado inicial S y el objetivo G,
Procedimiento GPS: Toma como argumento al objetivo G.
1) Mientras S no sea equivalente a G
a) D:= una diferencia entre S y G.
b) R:= una regla (F-rule) relevante para reducir D.
c) C:= parte izquierda o precondición de R.
d) Llamar a GPS tomando como argumento a C.
e) S:= resultado de aplicar R a S.

Algoritmo de búsqueda por diferencias (AMF)


1. Comparar actual con objetivo. Si no existen diferencias entre ellos TERMINAR.
2. Seleccionar la diferencia más importante y reducirla haciendo lo siguiente hasta
que se llegue a un éxito o a un fracaso:
a) Seleccionar un operador O aún no intentado que sea aplicable a la diferencia
actual. Si no existe O devolver FRACASO.
b) Intentar aplicar O a ACTUAL. Generar las descripciones de los 2 estados O-
antes que es un estado donde se satisfacen las precondiciones de O y O-
FINAL, el estado que resulta de la aplicación de O al estado O-ANTES.
c) PRIMERA_PARTE := AMF(ACTUAL, O-ANTES)-> llamado recursivo que
devuelve boolean.
ULTIMS_PARTE:=AMF(O-FINAL, OBJETIVO)
d) Si PRIMERA_PARTE Y ULTIMA_PARTE son exitosas anotar un éxito y
devolver el resultado de la concatenación de los operadores de
PRIMERA_PARTE y ULTIMA_PARTE.

87
Los análisis de reducción de diferencias se basan en un conjunto de reglas
STRIPS que pueden transformar un estado del problema en otro. Estas reglas
están formadas por las precondiciones (condiciones que deben cumplirse para
poder aplicarla) y los resultados. Analicemos el ejemplo de un dominio sencillo de
un robot. Supongamos que están disponibles los siguientes operadores STRIPS:

- EMPUJAR(obj,loc)
Precondición: EN(robot,obj) ∧ GRAN(obj) ∧ DESPEJAR(obj) ∧ BRAZOVACIO
Suprimir: EN(robot,locant) ∧ EN(obj,locant)
Añadir: EN(obj,loc) ∧ EN(robot,loc)

- LLEVAR(obj,loc)
Precondición: EN(robot,obj) ∧ PEQUEÑO(obj)
Suprimir: EN(robot,locant) ∧ EN(obj,locant)
Añadir: EN(obj,loc) ∧ EN(robot,loc)

- CAMINAR(loc)
Precondición: ninguna
Suprimir: EN(robot,locant)
Añadir: EN(robot,loc)

- COGER(obj)
Precondición: EN(robot,obj) ∧ BRAZOVACIO
Suprimir: BRAZOVACIO
Añadir: SOSTENER(obj)

- DEJAR(obj)
Precondición: SOSTENER(obj)
Suprimir: SOSTENER(obj)
Añadir: BRAZOVACIO

- COLOCAR(obj1,obj2)
Precondición: EN(robot,obj2) ∧ SOSTENER(obj1)
Suprimir: SOSTENER(obj1)
Añadir: ENCIMA(obj1,obj2) ∧ BRAZOVACIO

Los predicados usados son los siguientes:


EN(robot,obj): el robot está situado al lado del objeto.
GRAN(obj): el objeto es de un tamaño grande.
PEQUEÑO(obj): el objeto es de un tamaño pequeño.
DESPEJAR(obj): el objeto no tiene nada encima.
EN(robot,loc): el robot está en una determinada localización.
SOSTENER(obj): el brazo del robot está sosteniendo al objeto.
ENCIMA(obj1,obj2): el objeto obj1 está encima del objeto obj2.
BRAZOVACIO: el brazo del robot está vacío.

La tabla de diferencias que describe qué operador u operadores reducen las


distintas diferencias es la siguiente:

88
EMPUJAR LLEVAR CAMINAR COGER DEJAR COLOCAR
Mover objeto x x
Mover robot x
Despejar objeto x
Poner objeto sobre objeto x
Vaciar brazo x x
Sostener un objeto x

Supongamos, ahora, el siguiente problema del robot: mover un escritorio de una


habitación a otra con dos objetos encima de él. Estos objetos deben moverse
también. En el estado inicial el robot está en una determinada posición con su
brazo vacío. La principal diferencia entre el estado inicial y el meta es la situación
del escritorio. Para reducir esta diferencia puede escogerse LLEVAR o EMPUJAR.
Supongamos que se escoge LLEVAR. Al intentar satisfacer sus precondiciones,
dos diferencias más deben reducirse: la situación del robot y el tamaño del
escritorio. La situación del robot puede reducirse aplicando CAMINAR, pero no
hay operador que permita cambiar el tamaño de un objeto, ya que el escritorio es
un objeto grande y se necesita que sea pequeño. Por lo tanto, debe escogerse
EMPUJAR. El progreso del resolutor de problemas en este punto es:

A B C D
----------------
Inicio EMPUJAR Meta

Hay que reducir las diferencias entre A y B y entre C y D. EMPUJAR tiene cuatro
precondiciones, que generan dos diferencias, pues ya el escritorio es grande y el
brazo se supone vacío. Podemos llevar al robot a la posición correcta usando
CAMINAR y la superficie del escritorio puede despejarse usando dos veces
COGER. Después del primer COGER, el intento de realizar el segundo produce
otra diferencia: el brazo debe estar vacío. Puede usarse DEJAR para reducir esta
diferencia. Después del segundo COGER, debemos nuevamente aplicar DEJAR
para que el brazo esté vacío. Una vez realizado EMPUJAR, el estado del problema
está cerca de la meta, pero no del todo. Para poner los objetos nuevamente en el
escritorio podemos usar COLOCAR. El progreso del resolutor de problemas en
este punto es:
Inicio Meta

A B C E D

Inicio CAMINAR COGER DEJAR COGER DEJAR EMPUJAR COLOCAR Meta

La diferencia final entre C y E puede reducirse usando CAMINAR para llevar al


robot de regreso a los objetos, seguido de COGER, LLEVAR y COLOCAR dos
veces. Hemos omitido el análisis de las diferencias. Su orden puede ser crítico,
por lo que es importante reducir primero las diferencias principales.

La secuencia de operadores final sería la siguiente:

89
Inicio
A B C
| | | | | | | ...
CAMINAR COGER DEJAR COGER DEJAR EMPUJAR

Meta
E D
... | | | | | | | | |
CAMINAR COGER LLEVAR COLOCAR CAMINAR COGER LLEVAR COLOCAR

En problemas complejos, el número de permutaciones de las diferencias es muy


grande y las tablas de diferencias pueden ser inmensas. Existen técnicas en las
cuales se extiende este enfoque de reducción de diferencias para problemas
complejos. Una de ellas es la planificación, la cual estudiaremos más adelante.

3.3.7. Búsqueda de soluciones múltiples.

En ocasiones necesitamos encontrar más de una solución al problema que


queremos resolver. Esta búsqueda podemos implementarla de dos formas:
- Removiendo el camino de búsqueda encontrado.
Este método consiste en lo siguiente:
1) Encontrar una solución utilizando algún método de búsqueda.
2) Eliminar del árbol de búsqueda todos los nodos que forman parte del
camino encontrado a la solución.
3) Ir al paso 1).
- Removiendo el último nodo del camino.
Este método es similar al anterior. Su única diferencia radica en que en vez
de eliminar el camino encontrado, se elimina sólo el último nodo de este
camino, repitiéndose nuevamente el proceso.

3.4. Búsqueda heurística.

Analicemos el problema del vendedor ambulante. Para resolverlo usando alguna


de las técnicas de búsqueda a ciegas estudiadas, se exploraría el árbol de todos
los posibles caminos y se devolvería aquel de menor longitud. Si existen N
ciudades, el número de caminos diferentes entre ellas es (N-1)!. Explorar cada
camino nos tomaría un tiempo proporcional a N y, por tanto, el tiempo total sería
proporcional a N!. Tomando 10 ciudades tardaríamos un tiempo de 10!=3
628 000 para solucionar el problema. Este fenómeno se llama explosión
combinatoria.

Para solucionar eficientemente la mayoría de los problemas difíciles, a menudo es


necesario comprometer los requerimientos de movilidad y sistematicidad y
construir una estructura de control que, aunque no nos garantice que
encontremos la mejor respuesta, por lo menos proporcione una respuesta
suficientemente buena.

90
3.4.1. Definición de heurística.

La palabra heurística viene del griego heuriskein que significa descubrir. Ella es
también el origen de eureka, que se deriva de la famosa exclamación de
Arquímedes eureka (“lo encontré”), que lanzó al descubrir un método para
determinar la pureza del oro. Otro libro dice que fue cuando Arquímedes salió
desnudo corriendo por la calle gritando eureka cuando descubrió el principio de
flotación mientras estaba en el baño. Feigenbaum y Feldman la definen así: “Una
heurística (regla heurística o método heurístico) es una regla para engañar,
simplificar o para cualquier otra clase de ardid, la cual limita drásticamente la
búsqueda de soluciones en grandes espacios de solución”.

La posibilidad de efectuar la búsqueda de una forma más eficiente se basa en el


uso de alguna información específica para el problema a solucionar. Esta
información, aunque imprecisa e incompleta, permite distinguir cuáles de los
nodos dirigen mejor el avance hacia la meta y permite realizar este avance
siempre en la dirección que momentáneamente tiene la mejor perspectiva. Se
llama búsqueda heurística a los métodos de búsqueda que usan este tipo de
información. Precisamente por esto a estos métodos, generalmente, se les llaman
métodos fuertes. Las técnicas heurísticas son como las guías turísticas: son
buenas cuando apuntan a direcciones interesantes y son malas cuando apuntan
a callejones sin salida. Usando buenas técnicas heurísticas, podemos esperar
lograr buenas soluciones a problemas difíciles, como el del vendedor ambulante,
en un tiempo menor que el exponencial.

Analicemos un ejemplo de heurística. Supongamos que un hombre se encuentra


en una extensa llanura y tiene sed. Caminando ha llegado a una pequeña
elevación que es la única en esa región y se sube a ella. Su objetivo es, por
supuesto, encontrar agua. Desde la elevación el hombre observa el panorama que
se presenta en la figura 1. ¿Qué dirección debe seguir para hallar el agua?.
Evidentemente la vegetación verde es un indicio de que en esa zona hay
humedad, por lo que es muy probable que exista agua en la superficie o
subterránea. Por otro lado, el movimiento de animales puede indicar que ellos se
dirigen allí a beber, por lo que, posiblemente, el agua está en la superficie. El
hombre, al usar esta información, puede decidir dirigirse primero hacia el norte, y
si no encuentra solución, o sea, no halla agua, dirigirse al oeste. En este caso, la
vegetación verde y el movimiento de animales han sido utilizadas como
heurística.

NORTE
Vegetación verde
Movimiento de animales
OESTE
ESTE
Vegetación verde HOMBRE 91
Vegetación amarilla

SUR
Fig. 3.10. Panorama visto por el hombre desde la elevación.

Argumentos a favor del uso de técnicas heurísticas


- Sin ellas estaríamos atrapados en la explosión combinatoria.
- Pocas veces necesitamos la solución óptima, usualmente una buena
aproximación satisface nuestras exigencias. De hecho, en la vida real las
personas, generalmente, no buscan la mejor solución a un problema; tan
pronto como encuentran una, abandonan la búsqueda. Por ejemplo, al
tratar de parquear un carro, cuando se encuentra un espacio disponible se
parquea, aunque más adelante exista uno mejor.
- Aunque las aproximaciones producidas por ellas pueden, en el peor de los
casos, no ser muy buenas, los peores casos surgen raramente en el mundo
real.

Esto conduce a otra forma de definir la I.A., como el estudio de técnicas para
resolver problemas exponencialmente difíciles en un tiempo polinomial
explotando el conocimiento sobre el dominio del problema.

Formas de incorporar la información heurística en un procedimiento de búsqueda


- En las reglas mismas. Por ejemplo, las reglas para el problema del ajedrez
pueden describir no solamente el conjunto de movimientos legales, sino
también un conjunto de movimientos “sensatos” según el escritor de las
reglas.
- Como una función heurística que evalúe los estados individuales del
problema y determine hasta qué punto son deseables. Las funciones
heurísticas bien diseñadas guían eficientemente el proceso de búsqueda en la
dirección más provechosa. El programa que las utilice puede intentar
minimizar dicha función o maximizarla según sea apropiado. Ejemplos de
funciones heurísticas son las siguientes:
Ajedrez: la ventaja de piezas nuestras sobre el oponente.
Rompecabezas de 8 piezas: el número de baldosas colocadas correctamente.
Vendedor Ambulante: la suma de las distancias recorridas hasta ahora o tomar
la distancia más pequeña de entre las de los sucesores del nodo actual.
Titafor: Puntuación otorgada de la siguiente forma: un punto para cada fila,
columna o diagonal en donde podamos ganar y en la que tengamos una pieza y
dos puntos para cada fila, columna o diagonal que sea posible ganadora y en la
que tengamos 2 piezas.

92
El papel exacto de las funciones heurísticas se estudiarán en cada uno de los
métodos de búsqueda heurística.

3.4.2. Búsqueda avara (greedy).

Este método consiste en realizar en cada momento la transición hacia el estado


que se encuentre más cerca del estado objetivo. Para esto se define una función
heurística que estime el costo de llegar de un estado al estado objetivo. Esta
función heurística h puede ser cualquiera, pero debe cumplir que h(n) = 0 si n es
el nodo objetivo.

Ejemplo: Búsqueda de la ruta más corta entre las ciudades Santiago y Morón.
(Ver figura 3.11. ) Supongamos que h es la distancia en línea recta entre las dos
ciudades. La ruta sería: Stgo, Bayamo, Holguín, Morón.

En general este método tiene un comportamiento bastante bueno y logra la


solución de forma rápida.

Deficiencias de la búsqueda avara

- No siempre la solución encontrada es la óptima. Ver ejemplo anterior. Note


que esta ruta no es la óptima (la mejor sería Santiago, Bayamo, Tunas,
Camagüey, Morón que tiene 32 km menos).
- Es incompleta, pues puede recorrer una ruta infinita y nunca llegar a la
solución. Ejemplo: si se quisiera la ruta entre Matanzas y Morón, la función
heurística aconseja tomar primero C.Habana. que conduce a un callejón sin
salida pues se mantiene todo el tiempo entre estas dos ciudades. Este
problema de que puede no terminar nunca, se debe a que como el algoritmo
sólo almacena el estado actual no tiene manera de saber dónde ha estado y
puede caer en un ciclo. Una forma de solucionar esta dificultad es mantener
una lista de estados visitados y nunca revisitar uno de éstos, lo cual garantiza
que el algoritmo terminará siempre que el espacio de estados del problema
sea finito.

93
Contramaestre
C. Hab. Mtzas
75 151 87
Stgo 140 Bayamo Holguín
99
92
118 80 211 142
97 Las Villas
138 Tunas 101 85
Palma 111 Ciego
Camagüey Morón
San Luis
Distancia en línea recta a Morón: Stgo( 366), Contramaestre(374), Palma(329),
Bayamo(253), Holguín(178), Tunas(193), Camagüey(101), C.Hab(224),
Mtzas(226), Las Villas(299), San Luis(160).

Figura 3.11. Problema de la ruta entre Stgo y Morón.

3. 4.3 Búsqueda por el mejor nodo (best-first search).

La búsqueda por el mejor nodo es una forma de combinar las ventajas de las
búsquedas en profundidad y de anchura en un único método. En cada paso del
proceso de búsqueda por el mejor nodo, seleccionamos el más prometedor de
aquellos nodos que se han generado hasta el momento. Esto se realiza aplicando
una función heurística apropiada a cada uno de ellos. Entonces expandimos el
nodo elegido usando las reglas para generar a sus sucesores. Si uno de ellos es
una solución podemos terminar. Si no, todos esos nodos se añaden al conjunto
de nodos generados hasta ahora. Se selecciona de nuevo el más prometedor y el
proceso continúa. Lo que sucede usualmente es que se realiza un proceso de
búsqueda en profundidad mientras se explora una rama prometedora. Como
ocasionalmente no se encuentra solución, se explora otra rama previamente
ignorada y que ahora parece más prometedora.

En este método de búsqueda la función heurística que estima los méritos de cada
nodo generado está definida como:
f(n) = g(n) + h(n),
donde g(n) es una medida del costo del camino desde el nodo inicial al nodo
actual n y h(n) es un estimado del costo adicional de llegar desde el nodo n al
nodo meta. g(n) no es, necesariamente, el costo del camino óptimo desde el nodo
inicial al nodo actual, pues puede haber caminos mejores no recorridos todavía.
g(n) simplemente se calcula como la suma de los costos de los arcos del camino
actual. La función h(n), sin embargo, es típicamente heurística y explota el
conocimiento sobre el domino del problema, ya que el “mundo” entre el nodo n y
el meta no ha sido explorado. Por supuesto, no existe un método general para
construir h(n), sino que depende del problema particular.

Al algoritmo de búsqueda que utiliza la función f(n) se le llama algoritmo A. Este


algoritmo no garantiza encontrar el camino óptimo a la solución, pues éste sólo
calcula un estimado del costo para alcanzar el nodo objetivo.

94
Se puede definir la función f*(n) que, en cualquier nodo n, calcule el costo real del
camino óptimo desde el nodo inicial al nodo n (g*(n)) más el costo del camino
óptimo desde el nodo n al nodo objetivo (h*(n)), es decir
f*(n) = g*(n) + h*(n),
donde h (n) es una función de evaluación heurística admisible, o sea, ∀n h*(n) es
*

menor o igual que la distancia real mínima a la meta. Es precisamente por esto
que el algoritmo que usa este estimador garantiza siempre encontrar, si existe, el
camino óptimo a la solución. A este algoritmo se le conoce con el nombre de
algoritmo A*.

La figura 3.12 muestra el principio de un procedimiento de búsqueda por el


mejor nodo. Los números que aparecen al lado de los nodos son los valores de
f(n). Note cómo en cada paso se selecciona el nodo de menor valor de f(n),
generando sus sucesores.

Paso 1 Paso 2 Paso 3

A A A

(3) B (5) C (1) D (3) B (5) C D

(4) E (6) F
Paso 4 Paso 5

A A

B (5) C D B (5) C D

(6) G (5) H (4) E (6) F (6) G (5) H E (6) F

(2) I (1) J

Fig. 3.12. Un ejemplo de búsqueda por el mejor nodo.

Para implementar este algoritmo hay que mantener, además de la lista de nodos
visitados, una lista de nodos generados pero no visitados. Esta lista permite al
algoritmo retomar un camino que fue abandonado temporalmente por otro que
parecía más promisorio. Este método de búsqueda consiste en: el mejor nodo de
la lista de nodos generados y no visitados (inicialmente el nodo raíz) se expande
generando sus sucesores y se colocan en la lista de nodos visitados; la función de
evaluación heurística se aplica a todos los sucesores y ellos son insertados en la
lista de nodos no visitados en orden de su costo. En el ciclo siguiente se
selecciona el mejor nodo de esta lista para ser procesado.

95
Algoritmo:
Nodo:= Nodo Inicial
While not Objetivo(Nodo) do
Begin
Colocar Nodo en la lista de nodos visitados;
Para cada sucesor de Nodo do
Insertar en cola de prioridades según f a (Sucesor, f(sucesor)); -> Lista de
nodos no visitados
Nodo:= cabeza de la cola de prioridades;
End;

Por ejemplo, para el problema del rompecabezas de 8 piezas puede tomarse h*(n)
como el número de piezas que no están en su posición final. Otra variante, que
contiene más información heurística pero requiere más cálculos, es la suma de
las distancias desde la posición actual de cada pieza a su posición correcta.

Ejemplo: Veamos nuevamente la figura 3.11. Si usamos como h la misma función


anterior y como g la suma de los costos (distancias) de las respectivas carreteras
veremos como en este caso, el algoritmo A* sí obtiene la ruta óptima Stgo,
Bayamo, Tunas, Camagüey, Morón. El árbol desarrollado está en la figura 3.13.

Stgo

Bayamo Palma
118+329 Contramaestre
140+253 75+374
= 393 = 447
= 449
Stgo Holguín
280+366 239+178
= 646 Contram. Tunas = 417
291+374 220+193
= 665 = 413
Bayamo
San Luis Camagüey
300+253 Morón
358+160 317+101
= 553
= 518 = 418

Figura 3.13. Algoritmo A* para el problema de la ruta Stgo- Morón.

Podemos hacer algunas observaciones interesantes sobre este algoritmo:

- Al incorporar g* en f* no siempre se elige como el siguiente nodo a expandir el


que parece más cercano a la meta. Esto es útil si nos preocupa el camino que
elijamos. Si sólo nos importa llegar a una solución de la forma que sea,
ponemos g*=0 y con esto estamos eligiendo el nodo más cercano a la meta
(búsqueda avara). Si queremos encontrar un camino en el menor número de
pasos, entonces ponemos el costo de un nodo a su sucesor constante igual a 1.

96
Si el costo de los operadores varía, se refleja en cada nodo y se calcula el
camino de costo mínimo.

- Cuando h* = 0 y g* = d, donde d es el nivel de profundidad del nodo, el


algoritmo A* es idéntico al método de búsqueda primero a lo ancho.

En Bratko puede verse la implementación de este algoritmo en PROLOG, así como


su aplicación para el problema del rompecabezas de 8 piezas. En Rich 1era
edición pág. 87-93 puede verse bien desarrollado el algoritmo A* para realizar la
búsqueda por el mejor nodo en un problema de grafos.

La principal desventaja del algoritmo A* es el requerimiento de memoria para


mantener la lista de nodos generados no procesados, por lo que, en este aspecto,
este algoritmo no es más práctico que la búsqueda primero a lo ancho. Una
variante del mismo es:

- Mantener sólo en la lista de nodos no visitados los mejores n nodos. El costo


de esta técnica es la pérdida de la solución óptima, pues un nodo malo
localmente se puede sacar de la lista y él podría conducir al óptimo global.
Esta técnica se conoce como beam search.

3.4.4. Búsqueda heurística en árboles y/o o algoritmo AO*.

Para encontrar soluciones en un árbol y/o necesitamos un algoritmo similar al


A*, pero con la capacidad de manejar los arcos Y apropiadamente. Este algoritmo
debería encontrar un camino desde del nodo inicial a un conjunto de nodos que
representan los estados meta. Nótese que puede ser necesario obtener más de un
estado meta, pues cada rama de un arco Y puede conducir a su propio nodo
meta.

Para ver por qué el algoritmo A* no es adecuado para la búsqueda en árboles y/o,
consideremos la figura 3.14.

Los números representan el valor de f* en cada nodo. El problema radica en que


la elección del nodo a expandir no sólo depende del valor de f* en ese nodo, sino
también de si ese nodo es parte del mejor camino actual a partir del nodo inicial.
El nodo simple más prometedor es C, pero no forma parte del mejor camino
actual, pues hay que incluir al nodo D.

A
(7)

(5) B (3) C (4) D

Fig. 3.14. Un árbol y/o donde no es posible utilizar A*.

97
Los nodos de un árbol y/o pueden ser clasificados en:
- nodos primitivos: son los que se corresponden con problemas que se resuelven
directamente.
- nodos muertos: son los que se corresponden con problemas no descomponibles
y que no tienen solución.
- nodos intermedios.

Para cada uno de ellos el estimado heurístico h*(n) se puede definir como:
- Si n es un nodo primitivo, entonces h*(n) = 0
- Si n es un nodo muerto, entonces h*(n) = ∞
- Si n es un nodo intermedio con j conectores, entonces h*(n) es el mínimo de los
costos calculados para cada uno de los j conectores. Si el conector es Y,
entonces su costo es:
∑ (cos to(n, n
k
k ) + h * ( n k )),

donde k es el número de nodos del conector. Si por el contrario, el conector es


O, entonces su costo es:
costo(n, nk) + h*(nk).

Para estimar la bondad de un nodo sólo se usa h*. No se usa g* como en el


algoritmo A*, pues no es posible calcular tal valor único, ya que puede haber
muchos caminos para un mismo nodo y además, tampoco es necesario, porque la
travesía hacia abajo del mejor camino conocido garantiza que sólo aquellos nodos
que estén en el mejor camino serán considerados para la expansión.

Si quisiéramos implementar este algoritmo usando un grafo y/o es necesario


realizar tres cosas en cada paso:
- Atravesar el grafo comenzando por el nodo inicial y siguiendo el mejor camino
actual, acumulando el conjunto de nodos que van en ese camino y aún no han
sido expandidos.
- Seleccionar uno de estos nodos no expandidos y expandirlo. Añadir sus
sucesores al grafo y calcular h* para cada uno de ellos.
- Cambiar la h* estimada del nodo recientemente expandido para reflejar la
nueva información proporcionada por sus sucesores. Propagar este cambio
hacia atrás a través del grafo. Para cada nodo que se visita mientras se va
avanzando en el grafo, decidir cuál de sus arcos sucesores es más prometedor
y marcarlo como parte del mejor camino actual, lo cual cambiará el mejor
camino actual.

En Rich 1era edición puede verse los pasos de este algoritmo expresados más
detalladamente. Bratko contiene una implementación del mismo en PROLOG.

La figura 3.15 muestra un ejemplo de una traza de este algoritmo. Los números
entre paréntesis indican el valor de h* en los nodos y los que no tienen paréntesis,
indican los costos de los arcos. Los nodos encerrados entre dos círculos son
nodos primitivos.

98
El algoritmo AO* no sirve cuando entre los subproblemas hay interacciones.

Diferencias entre el algoritmo A* y el AO*

En el algoritmo A* el camino deseado de un nodo a otro siempre era el de menor


costo. Pero éste no es siempre el caso cuando se está buscando en un árbol y/o.
Un ejemplo de esto se muestra en la figura 3.16. El nuevo camino al nodo 5 es
más largo que yendo a través del nodo 3. Pero el camino por el nodo 3 no lleva a
solución, pues necesita también una solución del nodo 4, la cual no existe. Por
tanto el camino a través del nodo 10 es mejor.
Paso 1 Paso 2

a a (2)
1 3

(1) b c (3)

Paso 3 Paso 4

a (4) a (6)
1 3 1 3

(3) b c (3) (9) b c (3)


1 1 1 1

d (0) (1) e d (0) e (7)


6

(1) h

Paso 5

a (8)
1 3

(9) b c (5)
1 1 2 1

(0) d (7) e f (2) g (0)


6 2 3

(1) h (0) i j (1)

Fig. 3.15. Una traza del algoritmo AO*.

99
1

2 3 4 nodo muerto

7 8 5 6

9 10

Fig. 3.16. Ejemplo que muestra las diferencias entre A* y AO*.

3.4.5. Búsqueda por ascenso de cima (hill-climbing).

[Tomado de Data Mining. Pieter Adriaans, Dolf Zantinge. Addison-Wesley


longman 1996 y de IA: un enfoque moderno]

Es de ascenso de cima si la función de evaluación de estado expresa calidad y se


llama de disminución de gradiente si esta función es de costo.

Una popular metáfora para esta búsqueda es el canguro en la niebla. Suponga un


canguro en un lugar desconocido con la tarea de encontrar la cima más alta.
Como hay poca visibilidad por la niebla, él identifica el punto más alto que él ve y
hacia allí salta. Desde esta nueva posición repite el proceso hasta que no haya un
punto más alto. Por supuesto, no garantiza el óptimo; depende de las
características del relieve concreto. Si el relieve es suave y existe un óptimo visible
se llega a él. Pero si es suave y existen muchos puntos similares puede no
llegarse. Lo mismo ocurre si existen muchos picos altos y el relieve es muy
abrupto. En estos casos se llega a óptimos locales. Una solución es el
paralelismo: colocar varios canguros que exploren el espacio de búsqueda. La
posibilidad de que uno alcance el punto más alto aumenta.

El algoritmo hill-climbing es un algoritmo de mejoramiento iterativo. Ellos se


caracterizan porque la misma descripción del estado contiene toda la información
necesaria para encontrar una solución. Es irrelevante la ruta a través de la cual
se obtiene la solución. La idea básica consiste en empezar con una configuración
completa y efectuar modificaciones para mejorar su calidad.

Algoritmo
1- Actual:= Estado inicial del problema
2- Siguiente:= sucesor de actual con mayor valor
3- Si valor(siguiente) < valor(actual) entonces
Devuelve actual y TERMINAR
sino
Actual:= siguiente
Ir al paso 2.

Este algoritmo termina cuando se llega a un punto donde no se puede obtener un


mejor valor. Al implementarse se hace con un ascenso de cima con reinicio

100
aleatorio, es decir, se ejecuta el algoritmo varias iteraciones comenzando desde
estados iniciales generados aleatoriamente y se guarda el mejor estado producido.

Desventajas:
- Máximos locales: Es una cima cuya altura es inferior a la cima más alta de todo
el espacio de estados. Una vez que ha alcanzado un máximo local, el algoritmo
para, aunque esta solución esté muy lejos de ser satisfactoria.
- Planicie: son áreas del espacio de estados en donde la función de evaluación
básicamente es plana. La búsqueda realizará un paseo al azar. En todos los
estados la función de evaluación da un valor muy similar y entonces se
escogerá cualquiera.
- Riscos: las laderas de algunos riscos tienen pendientes muy pronunciadas, por
lo que es fácil para una búsqueda llegar a la cima del risco; sin embargo puede
suceder que la pendiente de la cima se aproxime demasiado gradualmente a un
pico. A menos que existan operadores que se desplacen directamente por la
cima del risco, la búsqueda oscilará de un lado a otro obteniendo muy poco
avance.

Por ejemplo: en el problema de las 8 reinas se empieza con 8 reinas situadas en el


tablero y se procede, entonces a desplazar las reinas buscando disminuir la
cantidad de posibles ataques. Los operadores serían mover las reinas a otra
posición de su columna de manera que la cantidad de ataques sea mínima.

3.5. Planificación.

En secciones anteriores describimos el proceso de resolver problemas como una


búsqueda a través de un espacio de estados, en el que cada nodo representa una
descripción del estado completo del problema y cada operador describe una forma
de cambiar dicha descripción completa del estado. Para problemas sencillos
como, por ejemplo, el rompecabezas de 8 piezas, el manipular cada vez la
descripción completa del estado es fácil y razonable.

Sin embargo, para dominios de problemas más complicados, es importante que


seamos capaces de trabajar por separado sobre elementos pequeños de un
problema y combinar al final las soluciones parciales en una solución completa.
Si no podemos hacer esto, el número de combinaciones de los estados de las
componentes de un problema se volvería demasiado grande para poderlo manejar
en la cantidad de tiempo disponible. Existen dos formas en las que es importante
que seamos capaces de realizar esta descomposición.

En la primera es importante que, al movernos de un estado del problema al


siguiente, no tengamos que recalcular el nuevo estado completo. En vez de ello,
queremos considerar sólo aquella parte del estado que puede haber cambiado. No
es difícil calcular cómo debería cambiar el estado del rompecabezas de 8 piezas
después de cada movimiento, ni significa gran cantidad de trabajo el almacenar
explícitamente una nueva copia del estado con los cambios apropiados que se
han hecho. Pero si estamos considerando el problema de guiar un robot por una
casa, la situación es mucho más compleja. La descripción de un estado sería muy

101
extensa, pues debería incluir dónde está cada objeto en la casa junto con la
situación actual del robot. Una acción concreta por parte del robot cambiará sólo
una pequeña parte del estado total. Por ejemplo, si el robot empuja una mesa,
sólo cambiará la situación de la mesa y de los objetos que estaban sobre ella, pero
los restantes objetos no cambiarán. En lugar de escribir reglas que describan
transformaciones de un estado completo en otro, preferiríamos escribir reglas que
sólo describan las partes del estado afectadas y suponer que el resto permanece
constante.

La segunda forma importante en que la descomposición puede facilitar la solución


de problemas difíciles, es la división de un único problema difícil en varios
subproblemas que podemos esperar que sean más fáciles. El algoritmo AO* nos
proporciona una manera de hacerlo cuando los subproblemas están
completamente separados. Pero existen muchos problemas casi descomponibles,
o sea, que pueden dividirse en subproblemas con una pequeña cantidad de
interacción. Por ejemplo, el trasladar un conjunto de muebles fuera de una
habitación podría descomponerse en trasladar cada objeto por separado, pero si
hay un librero detrás de una mesa, debemos mover primero la mesa. Es necesario
un método que permita trabajar con este tipo de problema.

El uso de métodos que se centran en formas de descomponer el problema original


en subpartes apropiadas y en formas de almacenar y manejar las interacciones
entre las subpartes conforme se detectan durante el proceso de resolución de
problemas se llama Planificación.

Al igual que en los sistemas de resolución de problemas estudiados anteriormente,


los métodos de planificación necesitan de técnicas para elegir la mejor regla a
aplicar, aplicar la regla elegida, detectar cuándo se ha encontrado solución y
detectar callejones sin salida. Estos métodos usan generalmente como FRC los
strips, donde se estudiaron estas técnicas. La técnica más ampliamente usada
para elegir las reglas apropiadas a aplicar es aislar un conjunto de diferencias entre
el estado meta deseado y el estado actual, identificando las reglas que reducen esas
diferencias. Si hay varias, se explota alguna información heurística para seleccionar
entre ellas.

Analizaremos estos métodos en el mundo de bloques. Las acciones que se pueden


realizar son:
COGER(x)
Precondición: ENMESA(x) y DESPEJAR(x) y BRAZOVACIO
Suprimir: ENMESA(x) y BRAZOVACIO
Añadir: SOSTENER(x)
DEJAR(x)
Precondición: SOSTENER(x)
Suprimir: SOSTENER(x)
Añadir: ENMESA(x) y BRAZOVACIO
APILAR(x,y)
Precondición: DESPEJAR(y) y SOSTENER(x)
Suprimir: DESPEJAR(y) y SOSTENER(x)
Añadir: ENCIMA(x,y) y BRAZOVACIO

102
DESAPILAR(x,y)
Precondición: ENCIMA(x,y) y DESPEJAR(x) y BRAZOVACIO
Suprimir: ENCIMA(x,y) y BRAZOVACIO
Añadir: SOSTENER(x) y DESPEJAR(y)

Usaremos los sgtes predicados:


ENCIMA(a,b),
ENMESA(a),
DESPEJAR(a) [No hay nada sobre a],
SOSTENER(a) [el brazo está sosteniendo a] y
BRAZOVACIO.
En el mundo de bloques existen diversas declaraciones lógicas verdaderas:
[ ∃ x SOSTENER(x)] Æ ¬ BRAZOVACIO --- Si el brazo está sosteniendo algo
entonces no está vacío.
∀ x [ ENMESA(x) Æ ¬ ∃ y ENCIMA(x,y) ]--- Si un bloque está encima de la mesa
entonces no está encima de otro
bloque.
∀ x [¬ ∃ y ENCIMA(y,x) Æ DESPEJAR(x) ] --- Cualquier bloque que no tenga encima
bloques, está despejado.

Existen varias técnicas de planificación, como por ejemplo:


- Planificación usando pila de metas.
- Planificación no lineal usando un conjunto de metas.
- Planificación jerárquica.
- Planificación no lineal usando la estrategia del menor compromiso.
- Planificación por medio de colocación de restricciones.
- Planificación mediante tablas de triángulos.
- Metaplaneo.
- Cajas de plan.

Estudiemos los tres primeros métodos. Explicaciones de los restantes pueden


verse en [Ric88].

3.5.1. Planificación usando pila de metas.

En este método, el resolutor de problemas usa una única pila que contiene tanto
las metas como los operadores que las satisfacen. El resolutor de problemas
dispone también de una B.D. que describe la situación actual y un conjunto de
operadores descritos en forma de STRIPS (listas precondición, añadir y suprimir).

Veamos un ejemplo del mundo de bloques: supongamos que se tienen los estados
inicial y meta mostrados en la figura 3.17.

Estado inicial: ENCIMA(B,A) ∧ ENMESA(A) ∧ ENMESA(C) ∧ ENMESA(D) ∧


BRAZOVACIO
Estado meta: ENCIMA(C,A) ∧ ENCIMA(B,D) ∧ ENMESA(A) ∧ ENMESA(D)

Estado inicial Estado meta

103
B C B

A C D A D

Fig. 3.17. Un ejemplo del mundo de bloques.

Cuando empezamos a resolverlo la pila de metas es:


ENCIMA(C,A) y ENCIMA(B,D) y ENMESA(A) y ENMESA(D)

Descompogamos este problema en cuatro subproblemas, uno por cada meta. Las
dos metas ENMESA ya están satisfechas en el estado inicial (le llamaremos para
abreviar EMAD), por lo que sólo trabajamos con las dos restantes.

Dependiendo del orden en que se escojan se pueden crear dos pilas de metas:
(1) (2)
ENCIMA(C,A) ENCIMA(B,D)
ENCIMA(B,D) ENCIMA(C,A)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Para cada paso que haya tenido éxito debe proseguirse con la meta que está en la
cima de la pila. Cuando se halla una secuencia de operadores que la satisface, se
aplica dicha secuencia a la descripción del estado, produciendo una nueva
descripción. A continuación se explora la meta que está en la cima de la pila y se
intenta satisfacerla empezando en la situación producida como resultado de
satisfacer la primera meta. Este proceso continúa hasta que la pila de metas esté
vacía. Al comprobar la última meta, se compara la meta original con el estado
final derivado de la aplicación de los operadores escogidos. Si cualquiera de las
componentes de la meta no se satisface en ese estado, lo cual podría suceder si se
alcanzasen en cierto punto y se deshiciesen más tarde, se reinsertan en la pila las
partes de la meta aún no resueltas y se reanuda el proceso.

En el ejemplo, supongamos que elegimos la pila 1. La pila 2 también conduce a


una solución, de hecho encuentra una tan trivial que no es muy interesante.
Miramos si ENCIMA(C,A) es cierto en el estado inicial. Como no lo es, buscamos
los operadores que lo pueden hacer cierto: APILAR(C,A). Se coloca éste en lugar
de ENCIMA(C,A), pues estamos seguros de que después de aplicar éste, la meta se
cumple. La pila sería ahora:

APILAR(C,A)
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Las precondiciones de APILAR se convierten entonces en submetas: DESPEJAR(A)


∧ SOSTENER(C). Las separamos y escogemos una de ellas. Aquí es útil explotar
algún conocimiento heurístico. SOSTENER es muy fácil de hacer (basta dejar
alguna cosa y coger el objeto deseado) y es muy fácil de deshacer (para cualquier
cosa el robot necesita usar el brazo). Si satisfacemos primero SOSTENER y luego
lo otro, es probable que entonces SOSTENER no sea cierto. Por tanto, explotamos

104
la heurística de que si SOSTENER es una de las metas, se trate de satisfacer en
último lugar. Este tipo de información heurística podría incluirse en las mismas
listas precondición de los operadores, escribiendo las metas en el orden en que
deben satisfacerse. La pila de metas es ahora:

DESPEJAR(A)
SOSTENER(C)
DESPEJAR(A) ∧ SOSTENER(C)
APILAR(C,A)
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Como DESPEJAR(A) no es cierto y el único operador que lo hace cierto es


DESAPILAR(B,A), lo cual puede detectarse fácilmente mirando las listas Añadir de
cada operador, la pila de metas es:

ENCIMA(B,A)
DESPEJAR(B)
BRAZOVACIO
ENCIMA(B,A) y DESPEJAR(B) y BRAZOVACIO
DESAPILAR(B,A)
SOSTENER(C)
DESPEJAR(A) y SOSTENER(C)
APILAR(C,A)
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

ENCIMA(B,A) se satisface y lo quitamos de la pila. DESPEJAR(B) no está


explícitamente, pero un demostrador de teoremas puede demostrar que se
satisface a partir de los predicados iniciales y del axioma del mundo de bloques
que dice que cualquier objeto sin bloques encima está despejado. Por eso se quita
también de la pila. La tercera precondición de DESAPILAR (BRAZOVACIO)
también es cierta y se saca de la pila, junto con la meta combinada, una vez
comprobado que se satisface.

Como se satisfacen sus precondiciones, aplicamos el operador DESAPILAR(B,A)


(usando las listas suprimir y añadir) para producir un nuevo modelo del mundo y
lo anotamos en la secuencia de solución propuesta:

ENMESA(A) y ENMESA(C) y ENMESA(D) y SOSTENER(B) y DESPEJAR(A).

Anotamos, entonces, este operador en la secuencia de solución propuesta. La pila


es ahora:

SOSTENER(C)
DESPEJAR(A) y SOSTENER(C)
APILAR(C,A)

105
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Para satisfacer SOSTENER(C) se pueden usar COGER(C) y DESAPILAR(C,x),


donde x es cualquier bloque. No podemos saber cuál escoger, por lo que creamos
dos ramas del árbol de búsqueda:

(1) (2)
ENMESA(C) ENCIMA(C,x)
DESPEJAR(C) DESPEJAR(C)
BRAZOVACIO BRAZOVACIO
ENMESA(C) y DESPEJAR(C) y ENCIMA(C,x) y DESPEJAR(C) y
BRAZOVACIO BRAZOVACIO
COGER(C) DESAPILAR(C,x)
DESPEJAR(A) y SOSTENER(C) DESPEJAR(A) y SOSTENER(C)
APILAR(C,A) APILAR(C,A)
ENCIMA(B,D) ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Observe que en la pila (2) x aparece tres veces. Es importante emparejar un


mismo objeto con cada una de las x. Por eso es mejor renombrarlas para no
confundirse. ¿Cómo el programa decide por dónde seguir?. Nosotros sabemos que
COGER(C) es mejor que desapilarlo si C no tiene nada encima, pues para
desapilarlo hay que apilarlo, lo que implica una pérdida de esfuerzo. Supongamos
que se escoge la alternativa (2). Para satisfacer ENCIMA(C,x) hay que apilar C
sobre x. La pila sería:

DESPEJAR(x)
SOSTENER(C)
DESPEJAR(X) y SOSTENER(C)
APILAR(C,x)
DESPEJAR(C)
BRAZOVACIO
ENCIMA(C,x) y DESPEJAR(C) y BRAZOVACIO
DESAPILAR(C,x)
DESPEJAR(A) y SOSTENER(C)
APILAR(C,A)
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Una de las precondiciones de APILAR es SOSTENER(C). Esto es lo que estamos


intentando conseguir aplicando DESAPILAR, que requiere APILAR para que se
cumpla ENCIMA(C,x). Como hemos regresado a la meta original, este camino es
improductivo. Si C hubiese estado sobre otro bloque, se satisface ENCIMA(C,x)
sin APILAR y este camino hubiera conducido a una solución.

Regresemos, entonces, a la pila (1). ENMESA(C) y DESPEJAR(C) se satisfacen y


los quitamos de la pila. Para hacer cierto BRAZOVACIO se puede aplicar
APILAR(B,x) o DEJAR(B). Si miramos hacia delante queremos, en último término,

106
poner B sobre D, luego lo más eficiente es hacerlo ahora. El programa podría
hacer esto comparando los operadores que están compitiendo con el resto de la
pila. Si uno hace alguna de estas metas se escoge éste. Luego aplicamos
APILAR(B,D) emparejando a D con x en APILAR. La pila de metas es ahora:

DESPEJAR(D)
SOSTENER(B)
DESPEJAR(D) y SOSTENER(B)
APILAR(B,D)
ENMESA(C) y DESPEJAR(C) y BRAZOVACIO
COGER(C)
DESPEJAR(A) y SOSTENER(C)
APILAR(C,A)
ENCIMA(B,D)
ENCIMA(C,A) y ENCIMA(B,D) y EMAD

Como puede verse DESPEJAR(D) y SOSTENER(B) son ciertas. Se realiza,


entonces, APILAR(B,D) y el modelo del mundo es ahora:

ENMESA(A) y ENMESA(C) y ENMESA(D) y ENCIMA(B,D) y BRAZOVACIO

Ya se han satisfecho todas las precondiciones para COGER(C) y se ejecuta. Luego


se ejecuta APILAR(C,A). Ahora se empieza a trabajar con la segunda parte:
ENCIMA(B,D). Pero ésta ya se satisface, pues anteriormente comparamos con la
pila de metas, luego comprobamos la meta combinada y el resolutor de problemas
termina, devolviendo el plan:

DESAPILAR(B,A), APILAR(B,D), COGER(C) y APILAR(C,A).

Para problemas más difíciles estos métodos de usar información heurística y de


considerar interacciones entre metas no son adecuados. Veamos un ejemplo:
Supongamos se tienen los estados inicial y meta mostrados en la figura 3.18.

Estado inicial Estado meta

C B

A B C

Estado inicial: ENCIMA(C,A) ∧ ENMESA(A) ∧ ENMESA(B) ∧ BRAZOVACIO


Estado meta: ENCIMA(A,B) ∧ ENCIMA(B,C)

Fig. 3.18. Un ejemplo del mundo de bloques ligeramente más difícil.

Existen dos formas de comenzar a resolver este problema:


(1) (2)

107
ENCIMA(A,B) ENCIMA(B,C)
ENCIMA(B,C) ENCIMA(A,B)
ENCIMA(A,B) y ENCIMA(B,C) ENCIMA(A,B) y ENCIMA(B,C)

Supongamos elegimos la alternativa (1). La pila de metas es la siguiente:

DESPEJAR(C)
BRAZOVACIO
DESPEJAR(C) y BRAZOVACIO
DESAPILAR(C,A)
BRAZOVACIO
DESPEJAR(A) y BRAZOVACIO
COGER(A)
DESPEJAR(B) y SOSTENER(A)
APILAR(A,B)
ENCIMA(B,C)
ENCIMA(A,B) y ENCIMA(B,C)

Podemos eliminar de la pila las metas que ya han sido satisfechas y llegamos a
BRAZOVACIO de COGER(A). Para satisfacerla necesitamos DEJAR(C). Podemos
continuar quitando elementos hasta que la pila sea:

ENCIMA(B,C)
ENCIMA(A,B) y ENCIMA(B,C)

El estado actual es ahora:

ENMESA(B) y ENCIMA(A,B) y ENMESA(C) y BRAZOVACIO

y la secuencia de operadores aplicada hasta ahora es:

DESAPILAR(C,A), DEJAR(C), COGER(A) y APILAR(A,B).

Se comienza a trabajar con ENCIMA(B,C). Para esto hay que desapilar A de B y


apilar B sobre C. Cuando se alcance la meta ENCIMA(B,C) y se saque de la pila,
se habrá ejecutado la secuencia de operadores siguientes:

DESAPILAR(A,B), DEJAR(A), COGER(B) y APILAR(B,C)

El estado del problema será entonces:

ENCIMA(B,C) y ENMESA(A) y ENMESA(C) y BRAZOVACIO

Pero al comprobar ENCIMA(A,B) y ENCIMA(B,C) descubrimos que hemos


deshecho ENCIMA(A,B), la cual puede alcanzarse de nuevo aplicando COGER(A) y
APILAR(A,B).
El plan completo es el siguiente:

1- DESAPILAR(C,A) 6- DEJAR(A)

108
2- DEJAR(C) 7- COGER(B)
3- COGER(A) 8- APILAR(B,C)
4- APILAR(A,B) 9- COGER(A)
5- DESAPILAR(A,B) 10- APILAR(A,B)

Aunque este plan alcanza la meta deseada no lo hace eficientemente. Una


situación similar hubiera ocurrido de coger la alternativa (2). Existen dos
enfoques para llegar a un buen plan. Uno es repararlo, lo cual es fácil aquí, pues
podemos buscar aquellos lugares donde se realiza una operación que se deshace
inmediatamente y eliminar a ambas. En el ejemplo, eliminaremos 4 y 5 y luego 3
y 6. El plan resultante es óptimo para este problema. Sin embargo, hay
problemas donde estos operadores pueden estar lejos unos de otros, además de
que se gasta mucho esfuerzo en producir los pasos para después eliminarlos. Un
método de planificación que construye directamente planes eficientes es la
planificación no lineal usando un conjunto de metas.

3.5.2. Planificación no lineal usando un conjunto de metas.

La idea de este procedimiento es escoger, en primer lugar, el operador que se


aplicará último en la solución final y suponer que ya se han satisfecho todas las
submetas menos una. Entonces encuentra todos los operadores que podrán
satisfacer la submeta final. Naturalmente, cada una de ellas puede tener
precondiciones que deben cumplirse, de forma que debe generarse una nueva
meta combinada que incluya tanto esas precondiciones como las metas
originales. Este procedimiento suele generar un árbol de búsqueda bastante
frondoso, pues debe considerar todas las diferentes ordenaciones de las
submetas, así como todas las formas en que podría satisfacerse una meta dada.
Afortunadamente, por otra parte, muchos de los caminos pueden eliminarse con
bastante rapidez.

Apliquemos este procedimiento al problema de la figura 3.18. En el primer nivel


consideramos dos candidatos para el último operador a realizar. La última cosa a
hacer puede ser poner A sobre B o poner B sobre C. Si escogemos poner B sobre
C, entonces las precondiciones para esa operación, así como el predicado
ENCIMA(A,B) deben ser todos ciertos antes de la operación. Pero el brazo sólo
puede sostener un bloque, de forma que no puede sostener B si A está sobre B.
Esta inconsistencia puede detectarse mediante un demostrador de teoremas por
resolución. Este camino inconsistente puede podarse, de forma que nos
quedemos con un solo candidato. Así se analiza todo el problema. Una porción del
árbol de búsqueda para este problema usando este método de planificación se
muestra en la figura 3.19.

109
ENCIMA(A ,B)
ENCIMA (B,C)

DESPEJAR (B) DESPEJAR (C)


SOSTENER(A) SOSTENER (B)
ENCIMA (B,C) ENCIMA (A,B)

Fig. 3.19. Porción del árbol de búsqueda para el problema de la fig. 3.18.

Limitaciones de este algoritmo:


- Si las submetas no interaccionan, no tiene importancia el orden en que se
lleven a cabo los pasos de una de ellas con respecto a los pasos de la otra.
Entonces todas las permutaciones posibles parecerán razonables y no se
podará ninguna, generándose un árbol muy grande. Necesitamos una forma de
evitar considerar la mayoría de secuencias de solución, ya que son
permutaciones unas de otras, por lo que cualquiera de ellas es adecuada. Este
problema se resuelve con el método de planificación no lineal usando la
estrategia del menor compromiso.
- Debido a que este método no tiene manera de distinguir entre metas
importantes y triviales, puede perder mucho tiempo en los detalles de un plan
que podemos descubrir luego que es completamente inadecuado. Para resolver
esto, necesitamos un planificador que empiece trazando un plan que satisfaga
la parte principal de la meta y llene a continuación los detalles conforme se
vayan necesitando. Este método es, precisamente, la planificación jerárquica.

3.5.3. Planificación jerárquica.

Para resolver problemas difíciles, un resolutor de problemas puede tener que


generar planes largos. Para hacerlo eficientemente, es importante poder eliminar
algunos de los detalles del problema hasta haber encontrado una solución a los
puntos principales. Entonces puede realizarse un intento de llenar los detalles
apropiados.

Con este método se resuelve, primeramente, el problema completo, considerando


sólo aquellas precondiciones cuyo valor crítico sea el más elevado posible. Estos
valores reflejan la dificultad esperada al satisfacer la precondición. Para realizar
esto, deben seguirse exactamente los mismos pasos que seguía STRIPS, pero
ignorar simplemente las precondiciones inferiores a los puntos críticos; luego se
usa el plan construido como un esbozo del plan completo y se consideran las pre-
condiciones del siguiente nivel crítico. Continuamos este proceso de considerar

110
precondiciones cada vez menos críticas hasta haber considerado todas las
precondiciones de las reglas originales.

Evidentemente, la asignación de valores críticos apropiados es crucial para el


éxito de este método. Aquellas precondiciones que no pueda satisfacer ningún
operador son, evidentemente, las más críticas. Por ejemplo, si estamos intentando
resolver un problema que atañe a un robot que se mueve por una casa y estamos
considerando el operador PASARPUERTA, la precondición de que exista una
puerta grande para que el robot pueda pasar a través de ella es altamente crítica,
pues si no existe, no podemos hacer nada. Sin embargo, la precondición de que la
puerta esté abierta es poco crítica si disponemos del operador ABREPUERTA.

Dando los valores críticos a las reglas STRIPS, el proceso básico funciona de
manera similar a como lo hacen los planificadores no jerárquicos.

Hay otras técnicas de planificación como:

- Planificación no lineal usando la estrategia del menor compromiso.


- Planificación por medio de colocación de restricciones.
- Planificación mediante tablas de triángulos.
- Metaplaneo.
- Cajas de plan.

Pueden verse explicaciones de ellas en Rich.

3.5.4. Aplicaciones prácticas de la planificación.

Algunos planificadores han obtenido resultados convincentes en diversos


dominios. Por ejemplo:

DEVISER: en la planificación de actividades espaciales.


GARI: en el maquinado de piezas mecánicas.
ISIS: en manufactura.
KNOBS: en misiones tácticas de fuerzas aéreas.

Ejemplos de planificadores no jerárquicos son STRIPS, HACKER e INTERPLAN.


Entre los jerárquicos podemos mencionar ABSTRIPS, que elabora sus planes bajo
un esquema de jerarquía en espacios de abstracción, en donde el más alto en
jerarquía contiene un plan carente de detalles irrelevantes y el de más baja
jerarquía contiene una secuencia completa y detallada de operadores de
resolución de problemas. Otros jerárquicos son NOAH, el cual hace abstracción
de los operadores de solución de problemas y MOLGEN, que hace abstracción
tanto de los operadores como de los objetos en el espacio de problemas.

111
3.6. Teoría de juegos.

Los juegos tienen para muchos una inexplicable fascinación, y la idea de que los
computadores pudieran jugar ha existido al menos desde que éstos existen. El
famoso arquitecto de computadoras Charles Babbage, antes de tiempo, pensó en
programar su Máquina Analítica para jugar al ajedrez, y más tarde pensó
construir una máquina para jugar al titafor. Dos de los pioneros de la ciencia de
la información y de la computación contribuyeron a la incipiente literatura de
juegos por computadora: Claude Shannon escribió un artículo en el que describía
mecanismos que podían usarse en un programa que jugase al ajedrez y unos
años después, Alan Turing describió un programa para este juego, aunque nunca
lo construyó. Al principio de los 60, Arthur Samuel tuvo éxito al construir el
primer programa importante y operacional para jugar. Su programa jugaba a las
damas y, además de jugar las partidas, podía aprender de sus errores y mejorar
su actuación.

Había dos razones para que los juegos pareciesen ser un buen dominio en el cual
explorar la inteligencia de la máquina:
- Proporcionan una tarea estructurada en la que es muy fácil medir el éxito o el
fracaso.
- No es obvio que requieran gran cantidad de conocimientos. Se pensó que se
podrían resolver por búsqueda directa a partir del estado inicial hasta la
posición ganadora.

La primera de esas razones es aún válida, pero la segunda no es cierta para


juegos no muy sencillos. Por ejemplo en el problema del ajedrez, se tienen los
siguientes datos:

- Factor de ramificación promedio: aproximadamente 35.


- En un juego promedio, cada jugador podría hacer 50 movimientos.
- Para examinar el árbol completo, hay que examinar 35100 posiciones.

Por lo tanto, es evidente que un programa que realice una simple búsqueda
directa del árbol de juego, no podrá seleccionar ni siquiera su primer movimiento
durante el período de vida de su oponente. Es necesaria alguna clase de
procedimiento de búsqueda heurística.

Una manera de ver todos los procedimientos de búsqueda estudiados es que son
de generar-y-comprobar. En un extremo, el generador propone soluciones
completas y el comprobador las evalúa. En el otro extremo, el generador genera
movimientos individuales del espacio de búsqueda, cada uno de los cuales se
evalúa por el comprobador, eligiendo el más prometedor de ellos. Para mejorar la
efectividad de un programa resolutor de problemas basado en la búsqueda
existen dos opciones:

- Mejorar el procedimiento de generación, de forma que se generen sólo buenos


movimientos o caminos.
- Mejorar el procedimiento de comprobación para que sólo se reconozcan y
exploren los mejores movimientos o caminos.

112
En los programas de juegos es particularmente importante que se hagan ambas
cosas. Naturalmente en ellos, como en otros dominios de problemas, la búsqueda
no es la única técnica disponible. Por ejemplo, en el ajedrez tanto las aperturas
como los finales están casi siempre altamente estudiados, de manera que se juega
mucho mejor consultando una tabla en una B.D. que almacene modelos
comprobados. Así, para jugar un juego completo pueden combinarse ambas
técnicas.

Desafortunadamente, para juegos como el ajedrez, no es posible buscar hasta


encontrar el estado meta, ni aún disponiendo de un buen generador de
movimientos plausibles. En la cantidad de tiempo disponible, sólo es posible
buscar menos de 10 movimientos en el árbol (llamados capas en la literatura de
juegos). Así, para elegir el mejor movimiento, se utiliza una función de evaluación
estática, que usa toda la información disponible para evaluar posiciones
individuales del tablero, estimando la probabilidad de que conduzcan a la
victoria. Su función es similar a la de h* en el algoritmo A*. Naturalmente, la fun-
ción de evaluación estática puede aplicarse, simplemente, a las posiciones
generadas por los movimientos propuestos. Pero, como es difícil producir una
función realmente buena, es mejor aplicarla tantos niveles hacia abajo en el árbol
de juego como el tiempo lo permita. Por ejemplo, Turing propuso una función de
evaluación estática para el ajedrez que se basaba en la ventaja de piezas: sumar
los valores de las piezas negras (N), los valores de las piezas blancas (B) y calcular
B/N. Samuel propuso otra para las damas que consistía en una combinación
lineal de funciones simples. Estas funciones consideraban la ventaja de piezas, la
capacidad de avance, el control del centro, amenazas dobles y movilidad. A cada
una de ellas se le asignaba un peso:
C1*ventajapiezas + C2*avance + C3*controlcentro + ...,
pero Samuel no conocía los pesos correctos. Para ello usó un mecanismo simple
de aprendizaje, en el que se incrementaban los pesos de aquellas componentes
que habían sugerido movimientos que condujeron a victorias y decrementaba los
que condujeron a derrotas. Pero esto no es fácil, pues pudimos hacer un
movimiento muy malo y después el oponente realizar un error y nosotros ganar el
juego, por lo que no podemos dar crédito de nuestra victoria a esta jugada mala.
No obstante, el programa de Samuel era capaz de derrotar a su creador de vez en
cuando. Otra función de evaluación estática para el juego de damas podría ser:
0.7*(MD-DO) + 0.3*(MP-PO),
donde MD y DO son el número de damas nuestras y del oponente,
respectivamente y MP y PO es la cantidad de las restantes piezas nuestras y del
oponente, respectivamente.

Hemos discutido las dos componentes importantes de un buen programa de


juegos: un buen generador de movimientos plausibles y una buena función de
evaluación estática. Ambas deben incorporar gran cantidad de conocimientos
sobre el juego concreto que se está jugando. Pero, a menos que estas funciones
sean perfectas, también necesitamos un procedimiento de búsqueda que haga
posible prever tantos movimientos como sea posible para ver lo que podría
ocurrir. Para juegos unipersonales simples puede usarse el algoritmo A*, pero
para juegos bipersonales esto es imposible.

113
3.6.1. Conceptos principales.

En esta sección sólo consideraremos los juegos entre dos personas con
información completa, o sea, los juegos en que los jugadores realizan jugadas
alternadamente y ambos conocen completamente lo que han hecho y pueden
hacer, pues cada movimiento conduce a un conjunto de estados completamente
predecible, como por ejemplo, el ajedrez, las damas, el titafor, etc. No tendremos
en cuenta los juegos de azar.

La teoría de los juegos es una disciplina, en la cual el objeto de sus


investigaciones son los métodos de toma de decisión en las llamadas situaciones
de conflicto. La situación se llama de conflicto si en ella chocan los intereses de
varias, habitualmente dos, personas que persiguen objetivos opuestos.

Imagínese que se quiere jugar una partida de ajedrez con las blancas pero no
podemos estar presentes personalmente durante el juego. Se tiene un sustituto
que debe desarrollar la partida y cumplir todas las indicaciones, pero él no sabe
jugar bien al ajedrez y no es capaz de tomar decisiones independientes. Para que
el sustituto pueda desarrollar toda la partida hasta el final, deben dársele tales
indicaciones que prevean cualesquiera posiciones posibles en el tablero y
determinen para cada posición la jugada que debe hacerse. El sistema completo
de estas indicaciones es lo que se conoce como estrategia. La estrategia del
jugador constituye la descripción unívoca de su elección en cada situación posible
en la que él debe hacer una jugada.

Para comprender los principios que constituyen el fundamento de la elección de


su estrategia por cada jugador, examinemos un estado particular del juego entre
dos personas, en el cual el jugador A puede emplear las estrategias X1, X2, X3 y
X4, mientras que el jugador B puede emplear Y1, Y2, Y3 y Y4. Las ganancias que
puede alcanzar el jugador A se muestran en la tabla siguiente:

Y1 Y2 Y3 Y4 A(X)

X1 7 2 5 1 1
X2 2 2 3 4 2
X3 5 3 4 4 3*
X4 3 2 1 6 1

B(Y) 7 3* 5 6

Supongamos que el primer jugador elige la estrategia Xk. La ganancia L(Xk,y) va a


depender de la estrategia que elija el segundo jugador. Por ejemplo, con la
estrategia X1 las ganancias de A pueden ser 7, 2, 5 ó 1. Esto indica que,
independientemente de la respuesta de B a la estrategia X1, la ganancia nunca
será menor que 1.

114
L(X1,y)={7,2,5,1} representa la ganancia del primer jugador con la estrategia
X1.

Generalizando los razonamientos expuestos vemos que si el jugador A emplea la


estrategia Xk, asegura una ganancia A(Xk) igual al menor elemento del conjunto
L(Xk,y):

A(Xk)=min L(Xk,y)
y

En la teoría de juegos se supone que los jugadores obran con suficiente cuidado
evitando el riesgo infundado. En este caso, el primer jugador debe elegir la
estrategia que maximice A(X). Designando la ganancia garantizada del primer
jugador por ALFA y llamándole precio puro inferior del juego, se obtiene:

ALFA= max A(x) = max min L(x,y)


x x y

En la tabla el valor ALFA está marcado en la columna A(x) por un asterisco (*).

Pueden realizarse razonamientos análogos con respecto al segundo jugador. Pero,


en este caso, la matriz del juego indica las pérdidas, las que él trata de hacer
mínimas.

B(Yk)=max L(x,Yk)
x

Para asegurarse la pérdida menor, el jugador B debe minimizar B(Y). Designando


la magnitud de la pérdida por BETA y llamándole precio puro superior del juego, se
obtiene:

BETA= min B(y) = min max L(x,y)


y y x

El valor BETA está marcado con un asterisco en la fila B(y) de la tabla.

3.6.2. La partida como un árbol.

Cada partida puede ser representada como un árbol. Como estudiamos en la


sección 3.2.1.1, el árbol de juego representa los cursos alternativos que puede
tomar dicho juego. Los nodos de tales árboles corresponden a situaciones del
juego y los arcos a movimientos. La situación inicial del juego es el nodo raíz y
las hojas del árbol corresponden a posiciones terminales.

Usando esta representación y considerando que, como se vio en la sección


anterior, se puede en cada momento calcular los precios inferior y superior del
juego de modo que cada jugador pueda seleccionar la estrategia mejor, el
procedimiento a seguir puede basarse en que en cada nivel del árbol se calculen
estos precios. Esto garantiza poder decidir por qué rama del árbol en ese
momento conviene que el jugador tome. El problema principal es cómo calcular
esos precios, para lo cual hay dos variantes. Los precios pueden ser calculados
como un estimado a priori de cuán bueno es un estado del juego sin tener en

115
cuenta la bondad de los estados futuros. La otra variante es considerar, al
calcular el precio de un estado, sus consecuencias futuras. Obviamente, la
segunda vía resulta mejor. Esta última variante presupone generar, a partir de
una situación dada que se considerará el nodo inicial, los nodos sucesores de éste
hasta cierto nivel de profundidad, para lo cual se pueden usar los métodos de
búsqueda ya estudiados. El nivel de profundidad al que se realiza el corte puede
ser llegar a una posición final, pero esto sólo es posible en juegos muy simples. Si
los nodos hojas no corresponden a posiciones finales es necesario calcular el
precio de éstos y propagar los mismos hasta el nodo inicial para entonces aplicar
el criterio de los precios superior e inferior por cada jugador.

El objetivo al usar un árbol de búsqueda es encontrar un buen primer


movimiento, para lo cual generamos un árbol hasta cierta profundidad, la cual
estará determinada por ciertas condiciones de terminación basadas en diversos
factores, como por ejemplo, un límite de tiempo, límite de almacenamiento,
profundidad, etc. Después de terminar la generación del árbol de búsqueda, el
cual se diferencia del árbol de juego en que solamente está formado por una parte
de éste, se tiene que hacer un estimado del mejor primer movimiento. Este
estimado puede hacerse aplicando una función de evaluación estática a los nodos
hojas del árbol, la cual mida la bondad de la posición representada en el nodo
hoja y luego propagar estos valores hasta el nodo en que es necesario tomar la
decisión. Seguidamente se presentan dos procedimientos para hacer esto.

3.6.3. Procedimientos de tomas de decisiones en el juego.

3.6.3.1 Procedimiento MINIMAX.

Este procedimiento consiste en lo siguiente: se genera el árbol de búsqueda,


tomando como nodo inicial determinada situación de conflicto, o sea, a partir de
una posición dada usando el generador de movimientos plausibles, se genera un
conjunto de posiciones sucesivas posibles. Luego se evalúan los nodos hojas
según algún estimador heurístico, el cual estima las oportunidades de ganar de
algún jugador (función de evaluación estática). Este valor representa la bondad de
la situación representada en el nodo para uno de los dos jugadores y, a la vez, da
una medida de cuán mala es esa situación para el otro. Como uno de los
jugadores tratará de alcanzar una posición de alto valor y el otro de un valor bajo,
los jugadores son llamados MAX y MIN respectivamente. Cuando MAX juegue, él
seleccionará un movimiento que maximice el valor. Por el contrario, MIN
seleccionará un movimiento que minimice el valor. Es usual, en el análisis,
adoptar el convenio de que posiciones favorables para MAX sean indicadas por
valores positivos de la función de evaluación, mientras que posiciones favorables
para MIN causen valores negativos y valores cercanos a cero correspondan a
posiciones no favorables a ninguno de los jugadores.

Teniendo en cuenta estas ideas se puede realizar el proceso de propagación de los


valores calculados para los nodos terminales hacia posiciones superiores en el
árbol. Supóngase que se van a evaluar los nodos del penúltimo nivel del árbol, o
sea, el nivel en que están los nodos cuyos sucesores corresponden a nodos

116
terminales y por eso ya evaluados usando la función de evaluación. Si esos nodos
corresponden a una situación en la cual le toca jugar a MAX lo llamaremos MAX-
nodo y se le asignará como valor el máximo de las evaluaciones de sus nodos
sucesores. Si, por el contrario, corresponden a una situación en la cual está MIN
en posición de jugar lo llamaremos MIN-nodo y el valor asignado será igual al
mínimo de la evaluación de los nodos sucesores. Se continúan propagando
valores nivel a nivel hasta que finalmente se evalúan los nodos sucesores del
nodo inicial. Estamos asumiendo que a MAX le toca jugar al inicio, de modo que
MAX debe seleccionar como su primer movimiento el que corresponde al sucesor
con mayor valor. Esta regla puede ser formalizada de la forma siguiente:
denotemos el valor estáti-co de una posición P por v(P) y el valor propagado por
V(P). Sean P1,..,Pn las posiciones sucesoras de P.

V(P)= v(P) Si P es una posición terminal en el árbol de búsqueda (n=0).


V(P)= max V(Pi) Si P es una posición en la que a MAX le toca jugar.
i

V(P)= min V(Pi) Si P es una posición en la que a MIN le toca jugar.


i

Analicemos un ejemplo: Supongamos una función de evaluación estática que


devuelve un rango de valores desde -10 a 10, con 10 indicando una victoria para
nosotros, -10 para el oponente y 0 para empate. En la figura 3.18, como nuestra
meta es maximizar V(Pi), elegimos movernos hacia B, llevando de regreso el valor
de B hasta A. Podemos concluir, entonces, que el valor de A es 8, pues nos
permite movernos hacia una posición con este valor. Pero como sabemos que la
función de evaluación estática no es precisa, es mejor guiar la búsqueda más allá
de una sola capa. Hagámoslo en 2 capas. En lugar de aplicar la función de
evaluación estática en B, C y D, aplicamos en cada una de ellas el generador de
movimientos plausibles, produciendo el conjunto de posiciones sucesoras de cada
una de ellas. Aplicando a estas posiciones la función de evaluación estática
obtenemos la figura 3.19. Ahora debemos tener en cuenta que es al oponente al
que le toca decidir. Supongamos que hacemos el movimiento B. Entonces el
oponente debe elegir entre E, F y G. El elige F, pues trata de minimizar el valor de
la función estática. Por tanto, si elegimos B desembocamos en una posición muy
mala para nosotros. La figura 3.20 muestra el resultado de propagar los nuevos
valores hacia la raíz del árbol.

A A

B C D
B C D
(8) (3) (-2) E F G H I J K
(9) (-6) (0) (0) (-2) (-4) (-3)

Fig.3.20 Búsqueda en una capa Fig. 3.21 Búsqueda en dos capas

117
A (-2)
Capa maximizante

(-6) B (-2) C (-4) D


Capa minimizante

E F G H I J K
(9) (-6) (0) (0) (-2) (-4) (-3)

Fig. 3.22 Propagación hacia atrás de los valores

La maximización y minimización en capas alternas cuando las evaluaciones se


envían de regreso a la raíz, corresponden a las estrategias opuestas de ambos
jugadores y da a este método el nombre de MINIMAX.

Una manera de implementar este procedimiento es la siguiente:


Entrada: Posición actual y el jugador que le toca jugar, ya sea MIN o MAX.
Salida: La jugada a realizar o el resultado del juego.
1) Si la posición es terminal para el juego, RETORNAR EL RESULTADO DEL
JUEGO, relativo al jugador que le toca jugar.
2) Si le toca jugar a MIN, entonces:
a) Generar los sucesores de la posición actual.
b) Aplicar MINIMAX a cada una de estas posiciones, considerando que al
jugador MAX le toca jugar.
c) RETORNAR EL MINIMO DE ESTOS RESULTADOS.
3) Si le toca jugar a MAX, entonces:
a) Generar los sucesores de la posición actual.
b) Aplicar MINIMAX a cada una de estas posiciones, considerando que al
jugador MIN le toca jugar.
c) RETORNAR EL MAXIMO DE ESTOS RESULTADOS.

Una implementación de este procedimiento en PROLOG puede verse en [Bra86].

3.6.3.2 Procedimiento alfa-beta.

Supongamos que el factor de ramificación para el juego del ajedrez es 20 y que el


nivel de profundidad a explorar es 6 (3 para cada jugador). Entonces, tendremos
que evaluar estáticamente 3 200 000 hojas y aplicar el procedimiento MINIMAX a
168 421 nodos intermedios. Suponiendo que se realizan 160 000 evaluaciones
por segundo, se necesitarían 22 segundos para analizar 3 movimientos.
Extendiendo esta búsqueda a 3 1/2 movimientos, el tiempo se incrementa a 422
segundos, o sea, 7 minutos. Cada jugador sólo puede consumir 3 minutos en
cada jugada, por lo que aplicar este procedimiento se hace impracticable.

El procedimiento MINIMAX separa completamente el proceso de generación del


árbol de búsqueda de la evaluación de la posición. Esta separación lleva a que
éste sea ineficiente, pues usualmente no es necesario visitar todas las posiciones

118
en el árbol para calcular correctamente el valor minimax de la posición raíz. Esta
deficiencia puede ser reducida considerablemente si se ejecuta la evaluación de
los nodos terminales y el cálculo de los valores propagados simultáneamente con
la generación del árbol. Esta mejora se basa en la idea siguiente: supongamos
que hay dos movimientos alternativos y uno de ellos muestra que es claramente
inferior al otro, por lo que no es necesario, entonces, conocer cuán inferior es
para tomar la decisión correcta.

A (>3)
Capa maximizante

B C
(3) (>-5)
Capa minimizante
(3) D (5) E (-5) F G

Fig. 3.23 Un ejemplo de árbol de búsqueda para la poda alfa-beta

Considerando el árbol de búsqueda de la figura 3.23, supongamos que un nodo


terminal se evalúa tan pronto como éste es generado y que se propague su valor
en cuanto se pueda. Después de examinar a F, sabemos que el oponente tiene
garantizado un tanteo de -5 o menos en C. Pero, como tenemos garantiza-do un
tanteo de 3 en A si nos movemos por B, no necesitamos explorar el nodo G.
Naturalmente, el podar un nodo puede parecer que no justifique el gasto de
grabar los límites y examinarlos, pero si estuviésemos explorando un árbol de 6
niveles, entonces no sólo habríamos eliminado un nodo único sino un árbol
entero de tres niveles de profundidad.

Esta estrategia modificada se llama poda alfa-beta. Requiere el mantenimiento de


dos valores umbrales: uno que representa la cota inferior del valor que puede
asignarse en último término a un nodo maximizante, que llamaremos ALFA, y
otro que represente la cota superior que puede asignarse a un nodo minimizante,
que llamaremos BETA.

A
Capa maximizante
Alfa=3
B C
Beta=5 Capa minimizante
D E F 7>5 G H
(3) (5) (4)

Capa maximizante
I J M N
(5)
K L (7) (8) Capa minimizante
(0) (7)
Fig. 3.24. Atajos alfa-beta.

119
Al realizar la búsqueda representada en la figura 3.24, se explora el árbol entero
encabezado por B, y descubrimos que en A podemos esperar un tanteo de 3 como
mínimo (ALFA=3). Cuando este valor ALFA se pasa a F nos permite podar L.
Veamos por qué. Después de examinar a K, vemos que I proporciona un tanteo
máximo de 0, lo que significa que F produce un mínimo de 0, pero esto es menor
que el valor ALFA de 3, por lo que no necesitamos explorar más ramas de I. El
jugador maximizante sabe que no debe elegir C e I a partir de C, pues ir por B es
mejor. Después se examina J que produce un valor de 5 y se le asigna a F, ya que
5 > 0. Este valor se convierte en el valor BETA del nodo C y nos garantiza que el
valor de C será 5 o menos. A continuación se expande G y se examina primero M
con un valor de 7. Pero ahora se compara 7 con el valor BETA. Como es mayor y
el jugador que tiene el turno del nodo C está tratando de minimizar, entonces ese
jugador no elegirá G, que conduce a un tanteo de 7 como mínimo, puesto que
puede irse por F. Por tanto, no es necesario explorar ninguna de las otras ramas
de G.

Esta reducción en el esfuerzo de la búsqueda fue alcanzada manteniendo las


pistas de las cotas sobre los valores propagados. En general, como los sucesores
de un nodo van produciendo nuevos valores, las cotas pueden ser modificadas,
pero:
- los valores ALFA de los nodos MAX no pueden decrecer, y
- los valores BETA de los nodos MIN no pueden crecer.

Debido a estas restricciones podemos enunciar las reglas siguientes para podar la
búsqueda:
a) Puede podarse debajo de cualquier nodo MIN que tenga un valor BETA
menor o igual al valor ALFA de sus nodos MAX padres. El valor final
propagado de este nodo MIN será entonces el valor BETA. Este valor puede
no ser el mismo que el obtenido por una búsqueda MINIMAX completa, pero
su uso trae por consecuencia que se seleccione el mismo mejor movimiento.
b) Puede podarse debajo de cualquier nodo MAX que tenga un valor ALFA
mayor o igual al valor BETA de cualquiera de sus nodos MIN padres. El
valor final propagado de este nodo MAX será entonces el valor ALFA.

Cuando la búsqueda se interrumpe por la regla a) se dice que ocurrió un corte


alfa. Si, por el contrario, fue por la regla b) se dice que ocurrió un corte beta.
Empleando el procedimiento alfa-beta siempre se realiza un movimiento que es
tan bueno como podría haber sido encontrado por el procedimiento MINIMAX,
buscando a la misma profundidad pero realizando mucha menos búsqueda.

Para ejecutar una poda ALFA-BETA, al menos alguna parte del árbol de búsqueda
tiene que ser generado a la profundidad máxima, porque los valores ALFA y BETA
se basan en los valores estáticos de los nodos terminales. Por eso, algún tipo de
búsqueda primero en profundidad se emplea usualmente al utilizar este
procedimiento.

Además, la cantidad de cortes que pueden ser hechos durante una búsqueda
depende del grado en que los valores ALFA y BETA iniciales aproximen los valores

120
propagados finalmente. El valor final propagado del nodo inicial es idéntico al
valor estático de alguno de los nodos terminales. Si este nodo terminal es el
primero que se alcanza en la búsqueda primero en profundidad, la cantidad de
cortes sería máxima, lo cual provoca que se generen y evalúen una cantidad
mínima de nodos hojas.

En este procedimiento el número de hojas estáticamente evaluadas es


aproximadamente 2bd/2, donde b es el factor de ramificación y d la profundidad
del árbol de búsqueda. Si utiliza MINIMAX, hay que evaluar todas las bd hojas.
Por lo tanto, utilizando el procedimiento alfa-beta, podemos explorar el doble de la
profundidad del árbol con el mismo número de evaluaciones. Si el orden de las
ramas del árbol fuera, exactamente, el inverso del correcto, entonces el
procedimiento alfa-beta no ahorraría nada.

Una manera de implementar este procedimiento es la siguiente:


Entrada: Posición actual, el jugador que le toca jugar, ya sea MIN o MAX,
ALFA, BETA, el nivel actual de búsqueda (NIVEL) y la profundidad
máxima de búsqueda (MAXPROF).
Salida: La evaluación estática de la posición actual.
1) Si NIVEL = MAXPROF, entonces calcular la función de evaluación estática
en la posición actual y RETORNAR EL RESULTADO.
2) Si al jugador MIN le toca jugar, entonces:
a) Generar una lista de Sucesores de la posición actual.
b) Si ALFA > BETA o Sucesores está vacía, entonces TERMINAR y
RETORNAR BETA (poda ALFA).
c) BETAc:= ALFA-BETA(Sucesores[1], MAX, ALFA, BETA, NIVEL + 1)
d) BETA:= mín(BETA,BETAc)
e) Eliminar el primer elemento de la lista Sucesores e ir al paso 2b).
3) Si al jugador MAX le toca jugar, entonces:
a) Generar una lista de Sucesores de la posición actual.
b) Si ALFA > BETA o Sucesores está vacía, entonces TERMINAR y
RETORNAR ALFA (poda BETA).
c) ALFAc:= ALFA-BETA(Sucesores[1], MIN, ALFA, BETA, NIVEL + 1)
d) ALFA:= máx(ALFA,ALFAc)
e) Eliminar el primer elemento de la lista Sucesores e ir al paso 3b).

Una implementación de este procedimiento en PROLOG puede verla en


[Bra86].

La efectividad de ALFA-BETA depende del orden en que se examinen los caminos.


Si 1ro se ven los peores caminos no se hará ninguna poda. Pero si conociéramos el
mejor camino no se haría ninguna búsqueda. No obstante, es de gran ayuda.

Profundización iterativa.
Se introdujo en CHESS 4.5 (Slate y Atkin 1977). En lugar de buscar en el árbol de
juego con una profundidad fija comienza buscando en una sola capa y luego aplica
la función de evolución estática. Entonces hace lo mismo para dos capas, tres, etc.
Se llama así porque en cada iteración la búsqueda se hace más profunda. A 1ra
vista parece un despilfarro pero tiene sus ventajas:

121
ƒ Debido a las restricciones de tiempo, cuando se considere fuera de tiempo,
puede cortarse la búsqueda en cualquier momento. En una profundidad fija no
puede estimarse el tiempo que se va a llevar.
ƒ Si se juzga que un movimiento es superior a sus hermanos en la iteración
previa, puede buscarse en él 1ro en la próxima iteración. Con este ordenamiento
la poda ALFA-BETA es más efectiva.

Se han desarrollado sistemas para jugar al ajedrez, damas, go, backgammon,


poker, othello (tablero de 8x8 con cuadros de dos colores), bridge, dominó, go mo
ku y corazones.

3.6.3.3. Refinamientos adicionales.

Además de la poda alfa-beta existen otros refinamientos del procedimiento


MINIMAX que mejoran su rendimiento: esperando el reposo, búsqueda
secundaria y usar movimientos de libro.

Esperando el reposo

Consideremos el árbol de la figura 3.25 y supongamos que, al expandir el nodo B,


obtenemos la figura 3.26. Nuestra estimación del nodo B cambió drásticamente,
lo cual pudiera suceder, por ejemplo, en medio de un intercambio de piezas. Al
iniciar dicho intercambio la situación del oponente ha mejorado. Si nos
detenemos ahí, decidiremos que B no es un buen movimiento. Para asegurarnos
de que tales medidas a corto plazo no influyan indebidamente en nuestra
elección, deberíamos continuar la búsqueda hasta que no ocurra un cambio tan
drástico de un nivel al siguiente. A esto se le llama esperar el reposo. Así podría
obtenerse el árbol de la figura 3.27, donde B vuelve a ser un buen movimiento,
pues ya se ha realizado la otra mitad del intercambio de piezas.

A A

B C (-4) B C (0)
(6) (0)
(0) (-4)
E F
Fig. 3.25 Situación inicial.
Fig. 3.26 Situación después de expandir el nodo B.

122
A
Capa maximizante

(6) B (0) C
Capa minimizante

(6) E (7) F
Capa maximizante

G H I J
(5) (6) (7) (6)

Fig. 3.27 Situación obtenida al esperar el reposo.

Búsqueda secundaria

Otra forma de mejorar MINIMAX es realizar una doble comprobación del


movimiento elegido, para asegurarnos de que no haya una trampa escondida
unos cuantos movimientos más adelante de los que exploró la búsqueda original.
Supongamos que elegimos un árbol de juego a profundidad 6 y escogemos un
movimiento. Aunque habría sido costoso explorar el árbol completo a profundidad
8, no lo es buscar 2 niveles más en la única rama escogida, para asegurarnos de
que es realmente buena. Esta técnica se llama búsqueda secundaria.

Usar movimientos de libro

En ajedrez, tanto la apertura como los finales, están altamente estilizados. El


rendimiento de un programa puede mejorarse considerablemente si se le
proporciona una lista de movimientos, llamados movimientos de libro, que
deberían realizarse en dichos casos. Usando esta estrategia, conjuntamente con
MINIMAX para el medio juego, se obtienen programas muy efectivos.

3.6.4. Limitaciones de MINIMAX.

A pesar de todos los refinamientos estudiados, el procedimiento MINIMAX tiene


las siguientes limitaciones:
- Susceptible del efecto horizonte. Supongamos, por ejemplo, que hacemos un
intercambio de piezas en donde el oponente necesitará dos movimientos para
capturar nuestra pieza. En su turno es casi seguro que el oponente realizará el
primero de estos movimientos. Pero en nuestro turno podemos realizar un
ataque en algún otro lugar y el oponente puede respondernos sin completar el
intercambio de piezas y así sucesivamente, durante varias jugadas. Si la
búsqueda se detiene en ese punto, nunca notaremos que la pérdida de una
pieza nuestra es inevitable. Aunque la búsqueda hasta el reposo suele

123
ayudarnos a evitar esto, aún es posible, dada la profundidad finita de cualquier
búsqueda, arrastar algo importante más allá del horizonte, donde nunca sea
notado.
- Se basa principalmente en el supuesto de que el oponente siempre elegirá el
movimiento óptimo. Supongamos que estamos perdiendo la partida y que
debemos elegir entre dos movimientos, uno de los cuales es ligeramente menos
malo que el otro. Si realizando el movimiento más malo es más probable que el
oponente cometa un error, nuestra jugada se convertiría en una muy buena.
En situaciones similares es mejor correr el riesgo de que el oponente cometa el
error. Sin embargo, en este caso el procedimiento MINIMAX no elegirá el
movimiento deseado por nosotros. Para tomar correctamente estas decisiones,
es importante tener acceso a un modelo del estilo de juego individual del
oponente, de forma que pueda estimarse la probabilidad de los diversos errores
que éste pudiera cometer. Pero esto es muy difícil de proporcionar.

EJERCICIOS PROPUESTOS

1- Sea el problema del mundo de bloques mostrado en la figura 3.28. Suponga


que se tiene definido el operador MOVER(x,y) donde x es un bloque y y puede ser
un bloque o la mesa.
a) Represente el espacio de estados.
b) ¿Cuál es el recorrido de la búsqueda primero en profundidad?.
c) ¿Cuál es el recorrido de la búsqueda primero a lo ancho?.

A B

C B C

Estado inicial Estado final

Fig. 3.26. Un problema del mundo de bloques.

2- Trazar la ejecución del procedimiento generar del enfoque guiado por datos
cuando se intenta determinar si N es divisible por 2 dada la siguiente base de
reglas y la B.D. inicial que contiene los hechos N es divisible por 10 y N es
divisible por 12.

Base de reglas
R1: Si X es divisible por 12, entonces X es divisible por 6.
R2: Si X es divisible por 20, entonces X es divisible por 10.
R3: Si X es divisible por 6, entonces X es divisible por 2.
R4: Si X es divisible por 10, entonces X es divisible por 5.

124
3- Trazar la ejecución del procedimiento validar del enfoque guiado por objetivos
cuando se intenta determinar si N es divisible por 2 dada la base de reglas del
ejercicio anterior y la B.D. inicial que contiene los hechos N es divisible por 10 y N
es divisible por 12.

4- Supongamos que los tres primeros pasos de la ejecución del algoritmo A*


proporcionan la situación mostrada en la siguiente figura, donde A+B significa
que el valor de g* en ese nodo es A y el valor de h* es B.
a) ¿Qué nodo se expandirá en el próximo paso?.
b) ¿Podemos garantizar que se encontrará la mejor solución?.

Paso 1 Paso 2

A A

(1+4) B C (1+3) (1+4) B C (1+3)

D
(2+4)

Paso 3
A

(1+4) B C (1+3)

(2+2) E D (2+4)

5- Supongamos que tenemos un problema que queremos resolver usando un


procedimiento heurístico de búsqueda por el mejor nodo. Necesitamos decidir si
representarlo como árbol o como grafo. Supongamos que sabemos que, en
promedio, cada nodo distinto se generará A veces durante el proceso de
búsqueda. Sabemos también que si usamos un grafo nos tomará, en promedio, la
misma cantidad de tiempo el comprobar un nodo para ver si ya ha sido generado,
que el que toma procesar B nodos si no se realiza ninguna comprobación. ¿Cómo
podemos decidir si usar un grafo o un árbol?.

6- Consideremos el árbol de juego mostrado en la figura 3.27. Supongamos que el


jugador A es maximizante.
a) ¿Qué movimiento debería elegir?
b) ¿Qué nodos no haría falta examinar usando la poda alfa-beta?

125
A

B C D

E F G H I J K

L M N O P Q R S T U V W X Y

7 6 8 5 2 3 0 2 6 2 5 8 9 2

7- Dos jugadores están frente a una pila de objetos. El primer jugador divide la
pila original en dos pilas que no pueden tener la misma cantidad de elementos.
Cada jugador alternadamente hace lo mismo. El juego continúa hasta que todas
la pilas tengan uno o dos objetos, pues en este punto se hace imposible
continuar. El jugador que primero no pueda jugar es el perdedor.

a) Construya el árbol de juego considerando que la pila inicial tiene 7 objetos.


b) Si MIN es el primero en jugar, muestre cómo independientemente de lo que
éste haga en el transcurso del juego, MAX siempre gana.
c) ¿Es la situación anterior cierta para cualquier cantidad de objetos en la pila
inicial?
d) Defina una función de evaluación estática para este juego.
e) Aplique los procedimientos MINIMAX y ALFA-BETA al juego.

8- Consideremos el sistema tipo STRIPS al que se le proporciona el conjunto de


operadores siguientes:
PASAR-EXAMEN
Precondición y Suprimir: IGUAL(grado,x)
Añadir: DORMIR-TARDE ∧ IGUAL(grado,x-10)
ESCRIBIR-ARTICULO
Precondición: IGUAL(grado,x)
Suprimir: IGUAL(grado,x) ∧ DORMIR-TARDE
Añadir: IGUAL(grado,x+50)
También se tienen los siguientes axiomas acerca de su universo:
MAYOR(grado,80) ⇒ CAPAZ-DE-GRADUARSE
MENOR(grado,80) ⇒ ¬ CAPAZ-DE-GRADUARSE

Supongamos que este sistema está intentando, dada la condición inicial


grado = 70, satisfacer la meta DORMIR-TARDE y CAPAZ-DE-GRADUARSE.

¿Cómo podríamos resolverlo mediante el enfoque de planificación usando un


conjunto de metas?.

126
Bibliografía

[Ric88]- Inteligencia Artificial , Elaine.Rich, 1ra edición.


[Ric2da]-Inteligencia Artificial , Elaine.Rich, 2da edición.
[Bell] Modelos Computacionales Avanzados. Rafael Bello.
[Frenz]- Crash course for Artificial Intelligence and Expert Systems. Louis Frenzel.
[] - Bases de Datos y Sistemas Expertos, Tomo II.
[Ullm] - Data structures and algorithms, Aho, Hopcroft, Ullman.
[Bra86] - Prolog programming for AI, Ivan Bratko, epígrafes 15.1,15.2,15.3

[IPN88]

Resumen

En este capítulo el lector ha profundizado en los métodos de solución de


problemas. Ha conocido los pasos para modelar un problema en I.A., así como las
características de los distintos espacios de búsqueda. Ha aprendido además sobre
los métodos de búsqueda de soluciones en problemas donde el conocimiento
sobre su dominio esté representado en una de las formas estudiadas en el
capítulo anterior. Estos métodos de búsqueda como: búsqueda a ciegas,
búsqueda heurística, planificación y teoría de juegos, el lector debe saber
aplicarlos en la solución de problemas y conocer sus limitaciones, ventajas y
desventajas.

127

También podría gustarte