Seminario de Sistemas 1 Ing Oralia Zamora FIME

57
Árboles y Busquedas Un árbol de decisión es un modelo de predicción utilizado en el ámbito de la inteligencia artificial. Dada una base de datos se construyen diagramas de construcciones lógicas, muy similares a los sistemas de predicción basados en reglas, que sirven para representar y categorizar una serie de condiciones que ocurren de forma sucesiva, para la resolución de un problema. Un árbol de decisión tiene unas entradas las cuales pueden ser un objeto o una situación descrita por medio de un conjunto de atributos y a partir de esto devuelve una respuesta la cual en últimas es una decisión que es tomada a partir de las entradas. Los valores que pueden tomar las entradas y las salidas pueden ser valores discretos o continuos. Se utilizan más los valores discretos por simplicidad, cuando se utilizan valores discretos en las funciones de una aplicación se denomina clasificación y cuando se utilizan los continuos se denomina regresión. Lleva a cabo un test a medida que este se recorre hacia las hojas para alcanzar así una decisión. Contener nodos internos, nodos de probabilidad, nodos hojas y arcos. Un nodo interno contiene un test sobre algún valor de una de las propiedades. Un nodo de probabilidad indica que debe ocurrir un evento aleatorio de acuerdo a la naturaleza del problema, este tipo de nodos es redondo, los demás son cuadrados. Un nodo hoja representa el valor que devolverá el árbol de decisión y finalmente las ramas brindan los posibles caminos que se tienen de acuerdo a la decisión tomada. De forma más concreta, refiriéndonos al ámbito empresarial, podemos decir que los árboles de decisión son diagramas de

Transcript of Seminario de Sistemas 1 Ing Oralia Zamora FIME

Page 1: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Árboles y Busquedas

Un árbol de decisión es un modelo de predicción utilizado en el ámbito de la inteligencia artificial.

Dada una base de datos se construyen diagramas de construcciones lógicas, muy similares a los sistemas de predicción basados en reglas, que sirven para representar y categorizar una serie de condiciones que ocurren de forma sucesiva, para la resolución de un problema.

Un árbol de decisión tiene unas entradas las cuales pueden ser un objeto o una situación descrita por medio de un conjunto de atributos y a partir de esto devuelve una respuesta la cual en últimas es una decisión que es tomada a partir de las entradas.

Los valores que pueden tomar las entradas y las salidas pueden ser valores discretos o continuos.

Se utilizan más los valores discretos por simplicidad, cuando se utilizan valores discretos en las funciones de una aplicación se denomina clasificación y cuando se utilizan los continuos se denomina regresión.

Lleva a cabo un test a medida que este se recorre hacia las hojas para alcanzar así una decisión.

Contener nodos internos, nodos de probabilidad, nodos hojas y arcos. Un nodo interno contiene un test sobre algún valor de una de las propiedades.

Un nodo de probabilidad indica que debe ocurrir un evento aleatorio de acuerdo a la naturaleza del problema, este tipo de nodos es redondo, los demás son cuadrados.

Un nodo hoja representa el valor que devolverá el árbol de decisión y finalmente las ramas brindan los posibles caminos que se tienen de acuerdo a la decisión tomada.

De forma más concreta, refiriéndonos al ámbito empresarial, podemos decir que los árboles de decisión son diagramas de decisiones secuenciales nos muestran sus posibles resultados.

Éstos ayudan a las empresas a determinar cuales son sus opciones al mostrarles las distintas decisiones y sus resultados.

La opción que evita una pérdida o produce un beneficio extra tiene un valor.

Page 2: Seminario de Sistemas 1 Ing Oralia Zamora FIME

La habilidad de crear un opción, por lo tanto, tiene un valor que puede ser comprado o vendido.

El algoritmo ID3 es utilizado dentro del ámbito de la inteligencia artificial.

Su uso se engloba en la búsqueda de hipótesis o reglas en él, dado un conjunto de ejemplos.

El conjunto de ejemplos deberá estar conformado por una serie de tuplas de valores, cada uno de ellos denominados atributos, en el que uno de ellos, ( el atributo a clasificar ) es el objetivo, el cual es de tipo binario ( positivo o negativo, si o no, válido o inválido, etc. ).

De esta forma el algoritmo trata de obtener las hipótesis que clasifiquen ante nuevas instancias, si dicho ejemplo va a ser positivo o negativo.

ID3 realiza esta labor mediante la construcción de un árbol de decisión.

Los elementos son:

Nodos: Los cuales contendrán atributos. Arcos: Los cuales contienen valores posibles del nodo padre. Hojas: Nodos que clasifican el ejemplo como positivo o negativo.

El algoritmo

Id3(Ejemplos, Atributo-objetivo, Atributos )

Si todos los ejemplos son positivos devolver un nodo positivo.

Si todos los ejemplos son negativos devolver un nodo negativo.

Si Atributos está vacío devolver el voto mayoritario del valor del atributo objetivo en Ejemplos.

En otro caso:

Sea A Atributo el MEJOR de atributos

Para cada v valor del atributo hacer:

Sea Ejemplos(v) el subconjunto de ejemplos cuyo valor de atributo A es v

Si Ejemplos(v) esta vacío devolver un nodo con el voto mayoritario del Atributo objetivo de Ejemplos

Sino Devolver Id3(Ejemplos(v), Atributo-objetivo, Atributos/{A})

Page 3: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Obsérvese que la construcción del árbol se hace forma recursiva, siendo las tres primeras líneas y la penúltima los casos base que construyen los nodos hojas.

Elección del mejor atributo

La elección del mejor atributo se establece mediante la entropía.

Eligiendo aquel que proporcione una mejor ganancia de información.

La función elegida puede variar, pero en su forma más sencilla es como esta:

Donde p es el conjunto de los ejemplos positivos, n el de los negativos y d el total de ellos. se debe establecer si el logaritmo es positivo o negativo

Un ejemplo

Ej. Cielo Temperatura Humedad Viento Jugar tenis

D1 Sol Alta Alta Débil -

D2 Sol Alta Alta Fuerte -

D3 Nubes Alta Alta Débil +

D4 Lluvia Suave Alta Débil +

D5 Lluvia Baja Normal Débil +

D6 Lluvia Baja Normal Fuerte -

D7 Nubes Baja Normal Fuerte +

D8 Sol Suave Alta Débil -

Page 4: Seminario de Sistemas 1 Ing Oralia Zamora FIME

D9 Sol Baja Normal Débil +

D10 Lluvia Suave Normal Débil +

D11 Sol Suave Normal Fuerte +

D12 Nubes Suave Alta Fuerte +

D13 Nubes Alta Normal Débil +

D14 Lluvia Suave Alta Fuerte -

En ese caso el árbol finalmente obtenido seriá así:

Cielo / | \ / | \ Soleado / Nublado \ Lluvia / | \ / + Humedad Viento / \ | \ / \ | \ Alta/ \ Normal Fuerte | \ Débil / \ | \ - + - +

La programación lógica

Consiste en la aplicación del corpus de conocimiento sobre lógica para el diseño de lenguajes de programación.

Comprende dos paradigmas de programación: Declarativa funcional.

La programación declarativa gira en torno al concepto depredicado, o relación entre elementos.

La programación funcional se basa en el concepto defunción (que no es más que una evolución de los predicados), de corte más temático.

Page 5: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Es un tipo de paradigmas de programación dentro del paradigma de programación declarativa.

El resto de los subparadigmas de programación dentro de la programación declarativa son: programación funcional, programación basada en restricciones, programas DSL (de dominio específico) e híbridos.

La programación lógica gira en torno al concepto de predicado, o relación entre elementos.

La programación funcional se basa en el concepto de función (que no es más que una evolución de los predicados), de corte más matemático.

Históricamente, los ordenadores se han programado utilizando lenguajes muy cercanos a las peculiaridades de la propia máquina: operaciones aritméticas simples, instrucciones de acceso a memoria, etc.

Un programa escrito de esta manera puede ocultar totalmente su propósito a la comprensión de un ser humano, incluso uno entrenado.

Hoy día, estos lenguajes pertenecientes al paradigma de la Programación imperativa han evolucionado de manera que ya no son tan crípticos

Campos de aplicación

La programación lógica encuentra su hábitat natural en aplicaciones de inteligencia artificial o relacionadas:

• Sistemas expertos, donde un sistema de información imita las recomendaciones de un experto sobre algún dominio de conocimiento.

• Demostración automática de teoremas, donde un programa genera nuevos teoremas sobre una teoría existente.

• Reconocimiento de lenguaje natural, donde un programa es capaz de comprender (con limitaciones) la información contenida en una expresión lingüística humana, etc.

La programación lógica también se utiliza en aplicaciones más "mundanas" pero de manera muy limitada, ya que la programación tradicional es más adecuada a tareas de propósito general.

Fundamentos

La mayoría de los lenguajes de programación lógica se basan en la teoría lógica de primer orden, aunque también incorporan algunos comportamientos de orden superior.

Page 6: Seminario de Sistemas 1 Ing Oralia Zamora FIME

En este sentido, destacan los lenguajes funcionales, ya que se basan en el cálculo lambda, que es la única teoría lógica de orden superior que es demostradamente computable (hasta el momento).

Lenguajes

El lenguaje de programación lógica por excelencia esProlog, que cuenta con diversas variantes. La más importante es la programación lógica con restricciones, que posibilita la resolución deecuac iones lineales además de la demostración de hipótesis.

Demostración automática de teoremas, donde un programa genera nuevos teoremas sobre una teoría existente.

Reconocimiento de lenguaje natural, donde un programa es capaz de comprender (con limitaciones) la información contenida en una expresión lingüística humana, etc.

La programación lógica también se utiliza en aplicaciones más "mundanas" pero de manera muy limitada, ya que la programación tradicional es más adecuada a tareas de propósito general.

Recursividad

Anuncio de cacao con una imagen recursiva. La mujer muestra un paquete idéntico al del propio anuncio, conteniendo así a otra mujer que muestra otro paquete más pequeño, de forma recursiva.

Page 7: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Imagen recursiva formada por un triángulo.

Cada triángulo está compuesto de otros más pequeños, compuestos a su vez de la misma estructura recursiva.

Recurrencia, recursión o recursividad es la forma en la cual se especifica un proceso basado en su propia definición.

Siendo un poco más precisos, y para evitar el aparente círculo sin fin en esta definición:

Un problema que pueda ser definido en función de su tamaño, sea este N, pueda ser dividido en instancias más pequeñas (< N) del mismo problema y se conozca la solución explícita a las instancias más simples, lo que se conoce como casos base, se puede aplicar inducción sobre las llamadas más pequeñas y suponer que estas quedan resueltas.

Para que se entienda mejor a continuación se exponen algunos ejemplos:

Factorial(x: Entero): Sea N := x el tamaño del problema, podemos definir el problema de forma recurrente como x*Factorial(x - 1); como el tamaño de Factorial(x - 1) es menor que N podemos aplicar inducción por lo que disponemos del resultado. El caso base es el Factorial(0) que es 1.

Ordenación por fusión(v: vector): Sea N := tamaño(v), podemos separar el vector en dos mitades. Estas dos mitades tienen tamaño N/2 por lo que por inducción podemos aplicar la ordenación en estos dos subproblemas. Una vez tenemos ambas mitades ordenadas simplemente debemos fusionarlas. El caso base es ordenar un vector de 0 elementos, que está trivialmente ordenado y no hay que hacer nada.

En estos ejemplos podemos observar como un problema se divide en varias (>= 1) instancias del mismo problema, pero de tamaño menor gracias a lo cual se puede aplicar inducción, llegando a un punto donde se conoce el resultado (el caso base)..

Nota: aunque los términos "recursión" y "recursividad" son ampliamente empleados en el campo de la informática, el término correcto en

Page 8: Seminario de Sistemas 1 Ing Oralia Zamora FIME

castellano es recurrencia. Sin embargo este último término es algo más específico.

Los números naturales

Un ejemplo de conjunto definido de forma recurrente es el de los números naturales:

a) 0 pertenece a Nb) Si n pertenece a N, entonces n+1 pertenece a Nc) Si X verifica a) y b) , entonces N está incluido en X

Los números naturales es el conjunto de números enteros no negativos.

Funciones definidas de forma recurrente

Aquellas funciones cuyo dominio puede ser recursivamente definido pueden ser definidas de forma recurrente.

El ejemplo más conocido es la definición recurrente de la función factorial n!:

Con esta definición veamos cómo funciona esta función para el valor del factorial de 3:

3! = 3 · (3-1)! = 3 · 2! = 3 · 2 · (2-1)! = 3 · 2 · 1! = 3 · 2 · 1 · (1-1)! = 3 · 2 · 1 · 0! = 3 · 2 · 1 · 1 = 6

Algoritmo recursivo

Un método usual de simplificación de un problema complejo es la división de este en subproblemas del mismo tipo.

Esta técnica de programación se conoce como divide y vencerás y es el núcleo en el diseño de numerosos algoritmos de gran importancia, así como también es parte fundamental de la programación dinámica.

Page 9: Seminario de Sistemas 1 Ing Oralia Zamora FIME

El ejemplo del cálculo recursivo del factorial de un número llevado al campo de la programación, en este ejemplo C++:

int factorial(int x){ if (x > -1 && x < 2) return 1; // Cuando -1 < x < 2 devolvemos 1 puesto que 0! = 1 y 1! = 1 else if (x < 0) return 0; // Error no existe factorial de números negativos return x * factorial(x - 1); // Si x >= 2 devolvemos el producto de x por el factorial de x - 1}

El seguimiento de la recursividad programada es casi exactamente igual al ejemplo antes dado, para intentar ayudar a que se entienda mejor se ha acompañado con muchas explicaciones y con colores que diferencia los distintos sub-procesos de la recursividad.

X = 3 //Queremos 3!, por lo tanto X inicial es 3X >= 2 -> return 3*factorial(2); X = 2 //Ahora estamos solicitando el factorial de 2 X >= 2 -> return 2*factorial(1); X = 1 // Ahora estamos solicitando el factorial de 1 X < 2 -> return 1; [En este punto tenemos el factorial de 1 por lo que volvemos marcha atrás resolviendo todos los resultados] return 2 [es decir: return 2*1 = return 2*factorial(1)]return 6 [es decir: return 3*2 = return 3*factorial(2)*factorial(1)] // El resultado devuelto es 6

Algoritmo implementado en el lenguaje Prolog:

fact(0,1):-!.fact(N,F):-N1 is N-1,fact(N1,F1),F is N*F1.

Ejemplos de recurrencias

Resolución de ecuaciones homogéneas de primer grado, segundo orden:

a) Se pasan al primer miembro los términos an, an − 1, an − 2, los cuales también podrían figurar como an + 2, an + 1, an

b) Se reemplaza an por r2, an − 1 por r y an − 2 por 1, quedando una ecuación de segundo grado con raíces reales y distintas r1 y r2.

c) Se plantea

Page 10: Seminario de Sistemas 1 Ing Oralia Zamora FIME

d) Debemos tener como dato los valores de los dos primeros términos de

la sucesión: y . Utilizando estos datos ordenamos el sistema de 2x2:

La resolución de este sistema nos da como resultado los valores u0 y v0, que son números reales conocidos.

e) La solución general es:

El área de la programación es muy amplia y con muchos detalles.

Los programadores necesitan ser capaces de resolver todos los problemas que se les presente a través del computador aun cuando en el lenguaje que utilizan no haya una manera directa de resolver los problemas.

En el lenguaje de programación C, así como en otros lenguajes de programación, se puede aplicar una técnica que se le dio el nombre de recursividad por su funcionalidad.

Esta técnica es utilizada en la programación estructurada para resolver problemas que tengan que ver con el factorial de un número, o juegos de lógica.

Las asignaciones de memoria pueden ser dinámicas o estáticas y hay diferencias entre estas dos y se pueden aplicar las dos en un programa cualquiera.

Recursividad:La recursividad es una técnica de programación importante.

Se utiliza para realizar una llamada a una función desde la misma función.

Como ejemplo útil se puede presentar el cálculo de números factoriales. Él factorial de 0 es, por definición, 1.

Los factoriales de números mayores se calculan mediante la multiplicación de 1 * 2 * ..., incrementando el número de 1 en 1 hasta llegar al número para el que se está calculando el factorial.

El siguiente párrafo muestra una función, expresada con palabras, que calcula un factorial.

Page 11: Seminario de Sistemas 1 Ing Oralia Zamora FIME

"Si el número es menor que cero, se rechaza. Si no es un entero, se redondea al siguiente entero. Si el número es cero, su factorial es uno.

Si el número es mayor que cero, se multiplica por él factorial del número menor inmediato."

Para calcular el factorial de cualquier número mayor que cero hay que calcular como mínimo el factorial de otro número.

La función que se utiliza es la función en la que se encuentra en estos momentos, esta función debe llamarse a sí misma para el número menor inmediato, para poder ejecutarse en el número actual.

Esto es un ejemplo de recursividad.

La recursividad y la iteración (ejecución en bucle) están muy relacionadas, cualquier acción que pueda realizarse con la recursividad puede realizarse con iteración y viceversa.

Normalmente, un cálculo determinado se prestará a una técnica u otra, sólo necesita elegir el enfoque más natural o con el que se sienta más cómodo.

Claramente, esta técnica puede constituir un modo de meterse en problemas.

Es fácil crear una función recursiva que no llegue a devolver nunca un resultado definitivo y no pueda llegar a un punto de finalización.

Este tipo de recursividad hace que el sistema ejecute lo que se conoce como bucle "infinito".

Para entender mejor lo que en realidad es el concepto de recursión veamos un poco lo referente a la secuencia de Fibonacci.

Principalmente habría que aclarar que es un ejemplo menos familiar que el del factorial, que consiste en la secuencia de enteros.0,1,1,2,3,5,8,13,21,34,...,

Cada elemento en esta secuencia es la suma de los precedentes (por ejemplo 0 + 1 = 0, 1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, ...) sean fib(0) = 0, fib (1) = 1 y así sucesivamente, entonces puede definirse la secuencia de Fibonacci mediante la definición recursiva (define un objeto en términos de un caso mas simple de si mismo):

fib (n) = n if n = = 0 or n = = 1fib (n) = fib (n - 2) + fib (n - 1) if n >= 2

Por ejemplo, para calcular fib (6), puede aplicarse la definición de manera recursiva para obtener:

Page 12: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Fib (6) = fib (4) + fib (5) = fib (2) + fib (3) + fib (5) = fib (0) + fib (1) + fib (3) + fib (5) = 0 + 1 fib (3) + fib (5)+ fib (1) + fib (2) + fib(5) =+ 1 + fib(0) + fib (1) + fib (5) =+ 0 + 1 + fib(5) = 3 + fib (3) + fib (4) = 3 + 1 + fib (0) + fib (1) + fib (4) =+ fib (1) + fib (2) + fib (4) =+ 0 + 1 + fib (2) + fib (3) = 5 + fib (0) + fib (1) + fib (3) =+ 0 + 1 + fib (1) + fib (2) = 6 + 1 + fib (0) + fib (1) =+ 0 + 1 = 8

Obsérvese que la definición recursiva de los números de Fibonacci difiere de las definiciones recursivas de la función factorial y de la multiplicación.

La definición recursiva de fib se refiere dos veces a sí misma .

Por ejemplo, fib (6) = fib (4) + fib (5), de tal manera que al calcular fib (6), fib tiene que aplicarse de manera recursiva dos veces.

Sin embargo calcular fib (5) también implica calcular fib (4), así que al aplicar la definición hay mucha redundancia de cálculo.

En ejemplo anterior, fib(3) se calcula tres veces por separado.

Sería mucho mas eficiente "recordar" el valor de fib(3) la primera vez que se calcula y volver a usarlo cada vez que se necesite.

Es mucho mas eficiente un método iterativo como el que sigue parar calcular fib (n).

If (n < = 1)return (n);lofib = 0 ;hifib = 1 ;for (i = 2; i < = n; i ++){x = lofib ;lofib = hifib ;hifib = x + lofib ;} /* fin del for*/return (hifib) ;

Compárese el numero de adiciones (sin incluir los incrementos de la variable índice, i) que se ejecutan para calcular fib (6) mediante este algoritmo al usar la definición recursiva.

En el caso de la función factorial, tienen que ejecutarse el mismo numero de multiplicaciones para calcular n!.

Page 13: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Mediante ambos métodos: recursivo e iterativo.

Lo mismo ocurre con el numero de sumas en los dos métodos al calcular la multiplicación. Sin embargo, en el caso de los números de Fibonacci, el método recursivo es mucho mas costoso que el iterativo.

2.- Propiedades de las definiciones o algoritmos recursivos:

Un requisito importante para que sea correcto un algoritmo recursivo es que no genere una secuencia infinita de llamadas así mismo.

Claro que cualquier algoritmo que genere tal secuencia no termina nunca.

Una función recursiva f debe definirse en términos que no impliquen a f al menos en un argumento o grupo de argumentos.

Debe existir una "salida" de la secuencia de llamadas recursivas.Si en esta salida no puede calcularse ninguna función recursiva.

Cualquier caso de definición recursiva o invocación de un algoritmo recursivo tiene que reducirse a la larga a alguna manipulación de uno o casos mas simples no recursivos.

3.- Cadenas recursivas:

Una función recursiva no necesita llamarse a sí misma de manera directa.

En su lugar, puede hacerlo de manera indirecta como en el siguiente ejemplo:

a (formal parameters) b (formal parameters){ {. .b (arguments); a (arguments);. .} /*fin de a*/ } /*fin de b*/

En este ejemplo la función a llama a b, la cual puede a su vez llamar a a, que puede llamar de nuevo a b.

Así, ambas funciones a y b, son recursivas, dado que se llamas a sí mismo de manera indirecta.

Sin embargo, el que lo sean no es obvio a partir del examen del cuerpo de una de las rutinas en forma individual.

La rutina a, parece llamar a otra rutina b y es imposible determinar que se puede llamar así misma de manera indirecta al examinar sólo a a.

Pueden incluirse mas de dos rutinas en una cadena recursiva.

Page 14: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Así, una rutina a puede llamar a b, que llama a c, ..., que llama a z, que llama a a.

Cada rutina de la cadena puede potencialmente llamarse a sí misma y, por lo tanto es recursiva.

Por supuesto, el programador debe asegurarse de que un programa de este tipo no genere una secuencia infinita de llamadas recursivas.

4.- Definición recursiva de expresiones algebraicas:

Como ejemplo de cadena recursiva consideremos el siguiente grupo de definiciones:

una expresión es un término seguido por un signo mas seguido por un término, o un término solo un término es un factor seguido por un asterisco seguido por un factor, o un factor solo.

Un factor es una letra o una expresión encerrada entre paréntesis.

Antes de ver algunos ejemplos, obsérvese que ninguno de los tres elementos anteriores está definido en forma directa en sus propios términos.

Sin embargo, cada uno de ellos se define de manera indirecta. Una expresión se define por medio de un término, un término por medio de un factor y un factor por medio de una expresión.

De manera similar, se define un factor por medio de una expresión, que se define por medio de un término que a su vez se define por medio de un factor.

Así, el conjunto completo de definiciones forma una cadena recursiva.

La forma mas simple de un factor es una letra.

Así A, B, C, Q, Z y M son factores.

También son términos, dado que un término puede ser un factor sólo.

También son expresiones dado que una expresión puede ser un término sólo.

Como A es una expresión, (A) es un factor y, por lo tanto, un término y una expresión. A + B es un ejemplo de una expresión que no es ni un término ni un factor.

Page 15: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Sin embargo (A + B) es las tres cosas. A * B es un término y, en consecuencia, una expresión, pero no es un factor. A * B + C es una expresión, pero no es un factor.

Cada uno de los ejemplos anteriores es una expresión valida. Esto puede mostrarse al aplicar la definición de una expresión de cada uno.

Considérese, sin embargo la cadena A + * B. No es ni una expresión, ni un término, ni un factor.

Sería instructivo para el lector intentar aplicar la definición de expresión, término y factor para ver que ninguna de ellas describe a la cadena A + * B.

De manera similar, (A + B*) C y A + B + C son expresiones nulas de acuerdo con las definiciones precedentes.

A continuación se codificará un programa que lea e imprima una cadena de caracteres y luego imprima "valida" si la expresión lo es y "no valida" de no serlo.

Se usan tres funciones para reconocer expresiones, términos y factores, respectivamente.

Primero, sin embrago se presenta una función auxiliar getsymb que opera con tres parámetros: str, length y ppos. Str contiene la entrada de la cadena de cadena de caracteres; length representa el número de caracteres en str. Ppos apunta a un puntero pos cuyo valor es la posición str de la que obtuvimos un carácter la ultima vez.

Si pos < length, getsymb regresa el carácter cadena str [pos] e incrementa pos en 1. Si pos > = length, getsymb regresa un espacio en blanco.

getsymb (str, length, ppos)char str[];int length, *ppos;{char C;if (*ppos < length)c = str [*ppos];elsec = ‘ ‘ ;(*ppos) ++;return ( c );} /* fin de getsymb*/

La función que reconoce una expresión se llama expr.

Page 16: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Regresa TRUE (o1) (VERDADERO) si una expresión valida comienza en la posición pos de str y FALSE (o0) FALSO en caso contrario.

También vuelve a colocar pos en la posición que sigue en la expresión de mayor longitud que puede encontrar.

Suponemos también una función readstr que lee una cadena de caracteres, poniendo la cadena en str y su largo en length.

Una vez descritas las funciones expr y readst, puede escribirse la rutina principal como sigue.

La biblioteca estándar ctype.h incluye una función isalpha que es llamada por una de las funciones siguientes.

# include <stdio.h># include <ctype.h># define TRUE 1# define FALSE =# define MAXSTRINGSIZE 100main (){char str [MAXSTRINGSIZE];int length, pos;readstr (str, &length);pos = 0;if (expr (str, length, &pos) = = TRUE && por >= length)printf ("%s", "valida");elseprintf ("%s", "no valida");/* La condición puede fallar por una de dos razones (o ambas).

Si expr(str, length, &pos) = = FALSE entonces no hay una expresión valida al inicio de pos.

Si pos < length puede que se encuentre una expresión valida, comenzando en pos, pero no ocupa la cadena completa */} /*fin del main*/

Las funciones factor y term se parecen mucho a expr excepto en que son responsables del reconocimiento de factores y término, respectivamente.

También reinicializan pos en la posición que sigue al factor o término de mayor longitud que se encuentra en la cadena str.

Los códigos para estas rutinas se apegan bastantes a las definiciones dadas antes.

Page 17: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Cada una intenta satisfacer uno de los criterios para la entidad que se reconoce.

Si se satisface uno de esos criterios el resultado es TRUE (VERDADERO). Si no satisface ninguno, el resultado es FALSE (FALSO).

expr (str. length, ppos)char str [];int length, *ppos;{/* buscando un término */if (term( str, length, ppos) = = FLASE)return (FLASE);/* se ha encontrado un término; revisar el siguiente símbolo */if (getsymb(str, length, ppos) ! = ‘+’) {/* se encontró la mayor expresión (un solo término). Reposicionar pos para que señale la última posición de la expresión */(*ppos) - - ;return (TRUE);} /* fin del if *//*

En este punto, se a encontrado un termino y su signo mas.

Se deberá buscar otro término */return (term(str, length, ppos));} /*fin de expr */

La rutina term que reconoce términos, es muy similar y será presentada sin comentarios.

term (str, length, ppos)char str[];int length, *ppos;{if (factor(str, length, ppos) = = FALSE)return (FALSE);if (getsymb (str, length, ppos) ! = ‘+’) {(*ppos) -- ;return (TRUE) ;} /* fin del if */return (factor(str, length, ppos));} /* fin de term */

La función factor reconoce factores y debería ser ahora bastante sencilla.

Usa el programa común de biblioteca isalpha (esta función se encuentra en la biblioteca ctype.h), que regresa al destino de cero si su carácter de parámetro es una letra y cero (o FALSO) en caso contrario.

Page 18: Seminario de Sistemas 1 Ing Oralia Zamora FIME

factor (str, length, ppos)char str[];int length, *ppos;{int c;if ((c = getsymb (str, length, ppos)) ! = ‘)’ )return (isalpha(c));return (expr(str, length, ppos) && getsymb (str, length, ppos) == ‘)’ );} /* fin de factor */

Las tres rutinas son recursivas, dado que cada una puede llamar a sí misma da manera indirecta.

Por ejemplo, si se sigue la acción del programa para la cadena de entrada " (a * b + c * d) + (e * (f) + g) " se encontrará que cada una de las tres rutinas expr, term y factor se llama a sí misma.

5.- Programación Recursiva:

Es mucho mas difícil desarrollar una solución recursiva en C para resolver un problema especifico cuando no se tiene un algoritmo.

No es solo el programa sino las definiciones originales y los algoritmos los que deben desarrollarse.

En general, cuando encaramos la tarea de escribir un programa para resolver un problema no hay razón para buscar una solución recursiva.

La mayoría de los problemas pueden resolverse de una manera directa usando métodos no recursivos.

Sin embargo, otros pueden resolverse de una manera mas lógica y elegante mediante la recursión.

Volviendo a examinar la función factorial.

El factor es, probablemente, un ejemplo fundamental de un problema que no debe resolverse de manera recursiva, dado que su solución iterativa es directa y simple.

Sin embargo, examinaremos los elementos que permiten dar una solución recursiva.

Antes que nada, puede reconocerse un gran número de casos distintos que se deben resolver.

Es decir, quiere escribirse un programa para calcular 0!, 1!, 2! Y así sucesivamente.

Page 19: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Puede identificarse un caso "trivial" para el cual la solución no recursiva pueda obtenerse en forma directa. Es el caso de 0!, que se define como 1.

El siguiente paso es encontrar un método para resolver un caso "complejo" en términos de uno mas "simple", lo cual permite la reducción de un problema complejo a uno mas simple.

La transformación del caso complejo al simple resultaría al final en el caso trivial.

Esto significaría que el caso complejo se define, en lo fundamental, en términos del mas simple.

Examinaremos que significa lo anterior cuando se aplica la función factorial. 4!

Es un caso mas complejo que 3!.

La transformación que se aplica al numero a para obtener 3 es sencillamente restar 1.

Si restamos 1 de 4 de manera sucesiva llegamos a 0, que es el caso trivial.

Así, si se puede definir 4! en términos de 3! y, en general, n! en términos de (n – 1)!, se podrá calcular 4! mediante la definición de n! en términos de (n – 1)! al trabajar, primero hasta llegar a 0! y luego al regresar a 4!.

En el caso de la función factorial se tiene una definición de ese tipo, dado que:n! = n * (n – 1)!

Asi, 4! = 4 * 3! = 4 * 3 * 2! = 4 * 3 * 2 * 1! = 4 * 3 * 2 * 1 * 0! = 4 * 3 * 2] * ] = 24

Estos son los ingredientes esenciales de una rutina recursiva: poder definir un caso "complejo" en términos de uno más "simple" y tener un caso "trivial" (no recursivo) que pueda resolverse de manera directa.

Al hacerlo, puede desarrollarse una solución si se supone que se ha resuelto el caso más simple.

La versión C de la función factorial supone que esta definido (n –1)! y usa esa cantidad al calcular n!.

Otra forma de aplicar estas ideas a otros ejemplos antes explicados.

En la definición de a * b, es trivial el caso de b = 1, pues a * b es igual a a.

En general, a + b puede definirse en términos de a * (b – 1) mediante la definición a * b = a * (b – 1) + a.

Page 20: Seminario de Sistemas 1 Ing Oralia Zamora FIME

De nuevo, el caso complejo se transforma en un caso mas simple al restar 1, lo que lleva, al final, al caso trivial de b = 1. Aquí la recursión se basa únicamente en el segundo parámetro, b.

Con respecto al ejemplo de la función de Fibonacci, se definieron dos casos triviales: fib(0) = 0 y fib(1) = 1. Un caso complejo fib(n) se reduce entonces a dos más simples: fib(n –1) y fib(n –2).

Esto se debe a la definición de fib(n) como fib(n –1) + fib(n – 2), donde se requiere de dos casos triviales definidos de manera directa.

Fib(1) no puede definirse como fib(0) + fib(-1) porque la función de Fibonacci no está definida para números negativos.

6.- Asignación estática y dinámica de memoria:

Hasta este momento solamente hemos realizado asignaciones estáticas del programa, y más concretamente estas asignaciones estáticas no eran otras que las declaraciones de variables en nuestro programa.Cuando declaramos una variable se reserva la memoria suficiente para contener la información que debe almacenar.

Esta memoria permanece asignada a la variable hasta que termine la ejecución del programa (función main).

Realmente las variables locales de las funciones se crean cuando éstas son llamadas pero nosotros no tenemos control sobre esa memoria, el compilador genera el código para esta operación automáticamente.

En este sentido las variables locales están asociadas a asignaciones de memoria dinámicas, puesto que se crean y destruyen durante la ejecución del programa.

Así entendemos por asignaciones de memoria dinámica, aquellas que son creadas por nuestro programa mientras se están ejecutando y que por tanto, cuya gestión debe ser realizada por el programador.

El lenguaje C dispone, como ya indicamos con anterioridad, de una serie de librerías de funciones estándar.

El fichero de cabeceras stdlib.h contiene las declaraciones de dos funciones que nos permiten reservar memoria, así como otra función que nos permite liberarla.

Las dos funciones que nos permiten reservar memoria son:malloc (cantidad_de_memoria);calloc (número_de_elementos, tamaño_de_cada_elemento);

Page 21: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Estas dos funciones reservan la memoria especificada y nos devuelven un puntero a la zona en cuestión.

Si no se ha podido reservar el tamaño de la memoria especificado devuelve un puntero con el valor 0 o NULL.

El tipo del puntero es, en principio void, es decir, un puntero a cualquier cosa.

Por tanto, a la hora de ejecutar estás funciones es aconsejable realizar una operación cast (de conversión de tipo) de cara a la utilización de la aritmética de punteros a la que aludíamos anteriormente.

Los compiladores modernos suelen realizar esta conversión automáticamente.

Antes de indicar como deben utilizarse las susodichas funciones tenemos que comentar el operador sizeof.

Este operadores imprescindible a la hora de realizar programas portables, es decir, programas que puedan ejecutarse en cualquier máquina que disponga de un compilador de C.

El operador sizeof (tipo_de_dato), nos devuelve el tamaño que ocupa en memoria un cierto tipo de dato, de esta manera, podemos escribir programas independientes del tamaño de los datos y de la longitud de palabra de la máquina.

En resumen si no utilizamos este operador en conjunción con las conversiones de tipo cast probablemente nuestro programa sólo funciones en el ordenador sobre el que lo hemos programado.

Por ejemplo, el los sistemas PC, la memoria está orientada a bytes y un entero ocupa 2 posiciones de memoria, sin embargo puede que en otro sistema la máquina esté orientada a palabras (conjuntos de 2 bytes.

Aunque en general una máquina orientada a palabras también puede acceder a bytes) y por tanto el tamaño de un entero sería de 1 posición de memoria, suponiendo que ambas máquinas definan la misma precisión para este tipo.

Con todo lo mencionado anteriormente veamos un ejemplo de un programa que reserva dinámicamente memoria para algún dato.

#include <stdlib.h#include <stdio.h>main(){int *p_int;float *mat;

Page 22: Seminario de Sistemas 1 Ing Oralia Zamora FIME

p_int = (int *) malloc(sizeof(int));mat = (float *)calloc(20,sizeof(float));if ((p_int==NULL)||(mat==NULL)){printf ("\nNo hay memoria");exit(1);}/* Aquí irían las operaciones sobre los datos *//* Aquí iría el código que libera la memoria */}Este programa declara dos variables que son punteros a un entero y a un float.

A estos punteros se le asigna una zona de memoria, para el primero se reserva memoria para almacenar una variable entera y en el segundo se crea una matriz de veinte elementos cada uno de ellos un float.

Obsérvese el uso de los operadores cast para modificar el tipo del puntero devuelto por malloc y calloc, así como la utilización del operador sizeof.

Como se puede observar no resulta rentable la declaración de una variable simple (un entero, por ejemplo, como en el programa anterior) dinámicamente.

En primer lugar por que aunque la variable sólo se utilice en una pequeña parte del programa, compensa tener menos memoria (2 bytes para un entero) que incluir todo el código de llamada a malloc y comprobación de que la asignación fue correcta (esto seguro que ocupa más de dos bytes).

En segundo lugar tenemos que trabajar con un puntero con lo cual el programa ya aparece un poco más engorroso puesto que para las lecturas y asignaciones de las variables tenemos que utilizar el operador *.Para termina un breve comentario sobre las funciones anteriormente descritas. Básicamente da lo mismo utilizar malloc y calloc para reservar memoria es equivalente:

mat = (float *)calloc (20,sizeof(float));mat = (float *)malloc (20*sizeof(float));

La diferencia fundamental es que, a la hora de definir matrices dinámicas calloc es mucho más claro y además inicializa todos los elementos de la matriz a cero.

Nótese también que puesto que las matrices se referencian como un puntero la asignación dinámica de una matriz nos permite acceder a sus elementos con instrucciones de la forma:NOTA: En realidad existen algunas diferencias al trabajar sobre máquinas con alineamiento de palabras.

Page 23: Seminario de Sistemas 1 Ing Oralia Zamora FIME

mat[0] = 5;mat[2] = mat[1]*mat[6]/67;Con lo cual el comentario sobre lo engorroso que resultaba trabajar con un puntero a una variable simple, en el caso de las matrices dinámicas no existe diferencia alguna con una declaración normal de matrices.

La función que nos permite liberar la memoria asignada con malloc y calloc es free(puntero), donde puntero es el puntero devuelto por malloc o calloc.

En nuestro ejemplo anterior, podemos ahora escribir el código etiquetado como:

/* Ahora iría el código que libera la memoria */free (p_int);free(mat);

Hay que tener cuidado a la hora de liberar la memoria. Tenemos que liberar todos los bloque que hemos asignado, con lo cual siempre debemos tener almacenados los punteros al principio de la zona que reservamos.

Si mientras actuamos sobre los datos modificamos el valor del puntero al inicio de la zona reservada, la función free probablemente no podrá liberar el bloque de memoria.

7.- Ejemplos:

7.1.- Las Torres de Hanoi:

A continuación se verá cómo pueden usarse técnicas recursivas para lograr una solución lógica y elegante de un problema que no se especifica en términos recursivos.

EL problema es el de "las torres de Hanoi", cuyo planteamiento inicial se muestra en la figura a continuación...

Hay tres postes: A, B y C. En el poste A se ponen cinco discos de diámetro diferente de tal manera que un disco de diámetro mayor siempre queda debajo de uno de diámetro menor.

Page 24: Seminario de Sistemas 1 Ing Oralia Zamora FIME

El objetivo es mover los discos al poste C usando B como auxiliar.

Sólo puede moverse el disco superior de cualquier poste a otro poste, y un disco mayor jamás puede quedar sobre uno menor.

Considérese la posibilidad de encontrar una solución. En efecto, ni siquiera es claro que exista una.Ahora se verá si se puede desarrollar una solución. En lugar de concentrar la atención en una solución para cinco discos, considérese el caso general de n discos. Supóngase que se tiene una solución para n – 1 discos y que en términos de ésta, se pueda plantear la solución para n – 1 discos. El problema se resolvería entonces. Esto sucede porque en el caso trivial de un disco (al restar 1 de n de manera sucesiva se producirá, al final, 1) la solución es simple: sólo hay que el único disco del poste A a C. Así se habrá desarrollado una solución recursiva si se plantea una solución para n discos en términos de n – 1. Considérese la posibilidad de encontrar tal relación. Para el caso de cinco discos en particular, supóngase que se conoce la forma de mover cuatro de ellos del poste A al otro, de acuerdo con las reglas. ¿Cómo puede completarse entonces el trabajo de mover el quinto disco? Cabe recordar que hay 3 postes disponibles.Supóngase que se supo cómo mover cuatro discos del poste A al C. Entonces, se pondrá mover éstos exactamente igual hacia B usando el C como auxiliar. Esto da como resultado la situación los cuatro primeros discos en el poste B, el mayor en A y en C ninguno. Entonces podrá moverse el disco mayor de A a C y por último aplicarse de nuevo la solución recursiva para cuatro discos para moverlo de B a C, usando el poste A como auxilia. Por lo tanto, se puede establecer una solución recursiva de las torres de Hanoi como sigue:Para mover n discos de A a C usando B como auxiliar:Si n = = 1, mover el disco único de A a C y parar.Mover el disco superior de A a B n – 1 veces, usando C como auxiliar.Mover el disco restante de A a C.Mover los disco n – 1 de B a C usando A como auxiliarCon toda seguridad este algoritmo producirá una solución completa por cualquier valor de n. Si n = = , el paso 1 será la solución correcta. Si n = = 2, se sabe entonces que hay una solución para n – 1 = = 1, de manera tal que los pasos 2 y 4 se ejecutaran en forma correcta. De manera análoga, cuando n = = 3 ya se habrá producido una solución para n – 1 = = 2, por lo que los pasos 2 y 4 pueden ser ejecutados. De esta forma se puede mostrar que la solución funciona para n = = 1, 2, 3, 4, 5,... hasta el valor para el que se desee encontrar una solución. Adviértase que la solución se desarrollo mediante la identificación de un caso trivial (n = = 1) y una solución para el caso general y complejo (n) en términos de un caso mas simple (n – 1).Ya se demostró que las transformaciones sucesivas de una simulación no recursivas de una rutina recursiva pueden conducir a un programa mas simple para resolver un problema. Ahora se simulara la recursión del problema y se intentara simplificar la simulación no recursiva.towers (n, frompeg, topeg, auxpeg)

Page 25: Seminario de Sistemas 1 Ing Oralia Zamora FIME

int n;char auxpeg, frompeg, topeg;{/* si es solo un disco, mover y regresar */if (n = = 1) {printf (" /n%s%c%s%c%", "mover disco 1 del poste",frompeg, "al poste", topeg);return; } /* fin del if*//* mover los n – 1 discos de arriba de A a B, usando como auxiliar */towers (n – 1, frompeg, auxpeg, tpoeg);/* move remaining disk from A to C */printf ("/n%s%d%s%c%s%c%", "mover disco", n, "del poste" frompeg, "al poste", topeg);/* mover n – 1 discos de B hacia C empleando a A como auxiliar */towers (n – 1, auxpeg, topeg, frompeg);} /* fin de towers */

En esta función, hay cuatro parámetros, cada uno de los cuales esta sujeto a cambios en cada llamada recursiva.

En consecuencia, el área de datos debe contener elementos que representen a los cuatro.

No hay variables locales. Hay un solo valor temporal que se necesita para guardar el valor de n – 1, pero esta se puede representar por un valor temporal similar en el programa de simulación y no tiene que estar apilada.

Hay tres puntos posibles a los que regresa la función en varias llamadas: el programa de llamada y los dos puntos que siguen a las llamadas recursivas.

Por lo tanto, se necesitan cuatro etiquetas.

start:Label1:Label2:Label3:La dirección de regreso se codifica como un entero (1, 2 o 3) dentro de cada área de datos.Considérese la siguiente simulación no recursiva de towers:struct dataarea {int nparam;char fromparam;char toparam;char auxparam;short int retaddr;};struct stack {int top struct dataarea item [MAXSTACK]; };simtowers (m, frompeg, topeg, auxpeg)

Page 26: Seminario de Sistemas 1 Ing Oralia Zamora FIME

int n;char auspeg, frompeg, topeg; {struct stack s;struct dataarea currarea;char temp;short int i;s.top = -1;currarea.nparam = 0;currarea.fromparam = ‘ ‘ ;currarea.toparam = ‘ ‘ ;currarea. auxparam = ‘ ‘ ;currarea.retaddr = 0;/* colocar en la pila un área de datos simulados */push (&s, &currarea);/* asignar parámetros y direcciones de regreso del área de datos actual a sus valores apropiados */currarea.nparam = n;currarea,fromparam = frompeg;currarea,toparam = topeg;currarea.auxparam = auxpeg;currarea.retaddr = 1;start: /* Este es el inicio de la rutina simulada */if (currarea.nparam = = 1) {printf (" /n%s%c%s%c", "mover disco 1 del poste", currarea.fromparam, "al poste", currarea.toparam) ;i = currarea.retaddr;pop (&s, &currarea);switch (i) {case 1: goto label1;case 2: goto label2;case 3: goto label3; } /* fin del switch */} /* fin del if *//* Esta es la primera llamada recursiva */push (&s, &currarea);-- currarea.nparam;temp = currarea.auxparam;currarea.auxparam = currarea.toparam;currarea.toparam = temp;currarea.retaddr = 2;got start;label2: /* se regresa a este punto desde la primera llamada recursiva */printf ("/n%s%d%s%c%s%c", "mover disco", currarea.nparam, "del poste", currarea.fromparam, "al poste", currarea.toparam);/* Esta es la segunda llamada recursiva */push (&s, &currarea);--currarea.nparam;temp = currarea.fromparam;currarea.fromparam = currarea.auxparam;currarea.auxparam = temp;currarea.rtaddr = 3;

Page 27: Seminario de Sistemas 1 Ing Oralia Zamora FIME

got start;label3: /* se regresa a este punto desde la segunda llamada recursiva */i = currarea.retaddr;pop (&s, &currarea);swicth (i) {case 1: goto label1;case 2: goto label2;case 3: goto label3; } /* fin del switch */label1: return;} /* fin de simtowers */

Ahora se simplificará el programa.

En primer lugar, debe observarse que se usan tres etiquetas para indicar direcciones de regreso; una para cada una de las dos llamadas recursivas y otra para el regreso al programa principal.

Sin embargo, el regreso al programa principal puede señalarse por un subdesborde en la pila, de la misma for que en la segunda versión simfact.

Esto deja dos etiquetas de regreso.

Si pudiera eliminarse otra mas, sería innecesario guardar en la pila la dirección de regreso, ya que solo restaría un punto al que se podría transferir el control si se eliminan los elementos de la pila con éxito.

Ahora dirijamos nuestra atención a la segunda llamada recursiva y a la instrucción:towers (n – 1, auxpeg, topeg, frompeg);Las acciones que ocurren en la simulación de esta llamada son las siguientes:

Se coloca el área de datos vigente a 1 dentro de la pila.

En la nueva área de datos vigente a 2, se asignan los valores respectivos n – 1, auxpeg, topeg, y frompeg a los parámetros.

En el área de datos vigente a 2, se fija la etiqueta de retorno a la dirección de la instrucción que sigue de inmediato a la llamada.

Se salta hacia el principio de la rutina simulada.

Después de completar la rutina simulada, ésta queda lista parar regresar.

Las siguientes acciones se llevan a efecto:

Se salva la etiqueta de regreso, /, de área de datos vigentes a 2.

Page 28: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Se eliminan de la pila y se fija el área de datos vigente como el área de datos eliminada de la pila, a 1.

Se transfiere el control a /.

Sin embrago, / es la etiqueta del final del bloque del programa ya que la segunda llamada a towers aparece en la última instrucción de la función.

Por lo tanto, el siguiente paso es volver a eliminar elementos de la pila y regresar.

No se volverá a hacer uso de la información del área de datos vigente a 1, ya que ésta es destruida en la eliminación de los elementos en la pila tan pronto como se vuelve a almacenar.

Puesto que no hay razón para volver a usar esta área de datos, tampoco hay razón para salvarla en la pila durante la simulación de la llamada.

Los datos se deben salvar en la pila sólo si se van a usar otra vez. En consecuencia, la seguida llamada a towers puede simularse en forma simple mediante:El cambio de los parámetros en el área de datos vigente a sus valores respectivos.

El "salto" al principio de la rutina simulada.

Cuando la rutina simulada regresa puede hacerlo en forma directa a la rutina que llamó a la versión vigente.

No hay razón para ejecutar un regreso a la versión vigente, sólo para regresar de inmediato a la versión previa.

Por lo tanto, se elimina la necesidad de guardar en la pila la dirección de regreso al simular la llamada externa (ya que se puede señalar mediante subdesborde y simular la segunda llamada recursiva, ya que no hay necesidad de salvar y volver a almacenar al área de datos de la rutina que llamada en este momento).

La única dirección de regreso que resta es la que sigue a la primera llamada recursiva.

Ya que sólo queda una dirección de regreso posible, no tiene caso guardarla en la pila para que se vuelva a insertar y eliminar con el resto de los datos.

Siempre se eliminan elementos de la pila con éxito, hay una solo dirección hacia la que se puede ejecutar un "salto" (la instrucción que sigue a la primera llamada).

Page 29: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Como los nuevos valores de las variables del área de datos vigente se obtendrán a partir de los datos antiguos de área de datos vigente, es necesario declarar una variable adicional, temp, de manera que los valores sean intercambiables.

7.2.- El Problema de las Ocho Reinas:

El problema de las ocho reinas y en general de las N reinas, se basa en colocar 8 reinas en un tablero de 8´ 8 (o N en un tablero de NxN, si el problema se generaliza), de forma que en no puede haber dos piezas en la misma línea horizontal, vertical o diagonal, ver Figura 1.

 Para ver el gráfico seleccione la opción "Descargar" del menú superiorFigura 1

Posible solución para el problema de las ocho reinas

Este programa has sido muy estudiado por los matemáticos. Se trata de un problema NP-Completo que no tiene solución para N=2 y N=3.

Para N=4 tiene una única solución. Para N=8 hay más de 80 soluciones dependiendo de las restricciones que se impongan.

Una forma de abordar el problema se lleva a cabo mediante la construcción de un predicado en Prolog del tipo siguiente: reinas (N, Solución), donde N representa las dimensiones del tablero y el número de reinas y Solución es una lista que contiene la permutación de la lista de números que resuelven el problema.

Los índices de dicha lista representan la columna en la que se encuentra una reina y el número que almacena la posición o índice representa la fila donde la reina está colocada. Así, para el ejemplo mostrado en la Figura 1, tenemos que R=[2,4,1,3].

Este problema es resuelto, de modo clásico, por el método de prueba y error, luego se adapta perfectamente a un algoritmo de backtracking.

Básicamente, el problema se reduce a colocar una reina, e intentar repetir el proceso teniendo en cuenta la reina colocada.

Si logramos poner todas las reinas el problema se ha resuelto, en caso contrario, deshacemos lo que llevamos hasta ahora y probamos con otra combinación.

Por tanto, hemos de generar un conjunto de permutaciones y seleccionar aquella que cumpla los requisitos impuestos por el juego.

Veamos el código que resuelve el problema:

rango(N, N, [N]).

Page 30: Seminario de Sistemas 1 Ing Oralia Zamora FIME

rango(N, M, [N|Cola]):-N<M, Aux is N+1, rango(Aux, M, Cola). dame(X,[X|Xs],Xs).dame(X,[Y|Ys],[Y|Zs]):-dame(X,Ys,Zs). permuta([],[]).permuta(L,[Z|Zs]):-dame(Z,L,Ys), permuta(Ys,Zs). atacada(X,Y):-atacada(X,1,Y).atacada(X,N,[Y|Ys]):-X is Y+N; X is Y-N.atacada(X,N,[Y|Ys]):-N1 is N+1, atacada(X,N1,Ys). correcta([]).correcta([X|Y]):-correcta(Y), not atacada(X,Y). reina(N, Solucion):-rango(1,N,L1), permuta(L1,Solucion), correcta(Solucion).

Es muy importante comprender como funciona cada uno de los predicados para entender el funcionamiento del algoritmo general.

Prolog permite implementar los programas casi directamente a partir de las especificaciones realizadas a partir de un análisis y diseño de la solución desde un alto nivel de abstracción.

Además el procedimiento de backtracking está implícito en el propio motor de inferencia, luego este paradigma se adapta perfectamente a nuestro problema.

Si analizamos y diseñamos nuestro problema tenemos que la forma de resolverlo se resume en los pasos siguientes:Para N, obtenemos una lista de números comprendidos entre 1 y N: [1,2,3,4,...,N].

Obtenemos una permutación del conjunto de números de la lista.Comprobamos que la permutación es correcta.

Si la permutación no es correcta, lo que debemos hacer es volver al paso 2 para generar una permutación nueva.

Comencemos a analizar la solución implementada. El problema se resuelve con el predicado reina(N, Solución): rango(1,N,L1), permuta(L1,Solucion), correcta Solución).

Como vemos es, sencillamente, una copia de las especificaciones realizadas más arriba. Se genera el rango entre 1 y N, se obtiene una permutación y se comprueba si la permutación es, o no, correcta.

En el caso de que cualquier predicado del consecuente falle, la propia máquina Prolog se encarga de realizar el proceso de backtracking.

Con lo cual ya tenemos cubiertos los cuatro pasos fundamentales del algoritmo.

Page 31: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Para tener más claras las ideas, observemos el árbol de ejecución general del objetivo reina(4,Solución) en la Figura 2.

Figura 2

Árbol de ejecución para el objetivo reina(4,Solución)

He aquí otro ejemplo sobre el problema de las ocho reinas.

Primero se mostrara un pseudocódigo sobre el mismo y luego su respectiva codificación en el lenguaje C.

<PRINCIPIO ensayar> (i: entero)inicializar el conjunto de posiciones de la reina i-ésima+-REPETIR hacer la selección siguiente| +-SI segura ENTONCES| | poner reina| | +-SI i < 8 ENTONCES| | | LLAMAR ensayar (i + 1)| | | +-SI no acertado ENTONCES| | | | quitar reina| | | +-FINSI| | +-FINSI| +-FINSI+-HASTA acertada O no hay más posiciones<FIN>

Observaciones sobre el código:

1) Estudiar la función ensayar() a partir de este pseudocódigo.

2) Vectores utilizados:

int posiciones_en_columna[8]; RANGO: 1..8BOOLEAN reina_en_fila[8]; RANGO: 1..8BOOLEAN reina_en_diagonal_normal[15]; RANGO: -7..7BOOLEAN reina_en_diagonal_inversa[15]; RANGO: 2..16

Page 32: Seminario de Sistemas 1 Ing Oralia Zamora FIME

En C, el primer elemento de cada vector tiene índice 0, esto

es fácil solucionarlo con las siguientes macros:#define c(i) posiciones_en_columna[(i)-1]#define f(i) reina_en_fila[(i)-1]#define dn(i) reina_en_diagonal_normal[(i)+7]#define di(i) reina_en_diagonal_inversa[(i)-2]

Significado de los vectores:

c(i) : la posición de la reina en la columna i

f(j) : indicativo de que no hay reina en la fila j-ésima

dn(k): indicativo de que no hay reina en la diagonal normal

(\) k-ésima

di(k): indicativo de que no hay reina en la diagonal

invertida (/) k-ésima

Dado que se sabe, por las reglas del ajedrez, que una reina actúa sobre todas las piezas situadas en la misma columna, fila o diagonal del tablero se deduce que cada columna puede contener una y sólo una reina, y que la elección de la situación de la reina i-ésima puede restringirse a los cuadros de la columna i.

Por tanto, el parámetro i se convierte en el índice de columna, y por ello el proceso de selección de posiciones queda limitado a los ochoposibles valores del índice de fila j.

A partir de estos datos, la línea poner reina del pseudocódigo es:

c (i) = j; f (j) = di (i + j) = dn (i - j) = FALSE;y la línea quitar reina del pseudocódigo:f (j) = di (i + j) = dn (i - j) = TRUE;

y la condición segura del pseudocódigo:

f (i) && di (i + j) && dn (i - j)/* Ficheros a incluir: */#include <stdio.h> /* printf () */#include <conio.h> /* getch () *//* Macros: */#define BOOLEAN int#define TRUE 1#define FALSE 0/* Variables globales: */

Page 33: Seminario de Sistemas 1 Ing Oralia Zamora FIME

BOOLEAN acertado;int posiciones_en_columna[8];BOOLEAN reina_en_fila[8];BOOLEAN reina_en_diagonal_normal[15];BOOLEAN reina_en_diagonal_inversa[15];#define c(i) posiciones_en_columna[(i)-1]/* rango de índice: 1..8 */#define f(i) reina_en_fila[(i)-1]/* rango de índice: 1..8 */#define dn(i) reina_en_diagonal_normal[(i)+7]/* rango de índice: -7..7 */#define di(i) reina_en_diagonal_inversa[(i)-2]/* rango de índice: 2..16 *//* Prototipos de las funciones: */void proceso (void);void ensayar (int i);/* Definiciones de las funciones: */void main (void){printf ("\n\nPROBLEMA DE LAS OCHO REINAS:\n ");proceso ();printf ("\n\nPulsa cualquier tecla para finalizar. ");getch ();}void proceso (void){register int i,j;for (i = 1; i <= 8; i++)f (i) = TRUE;for (i = 2; i <= 16; i++)di (i) = TRUE;for (i = -7; i <= 7; i++)dn (i) = TRUE;ensayar (1);if (acertado)for (printf ("\n\nLA SOLUCION ES:\n\n"), i = 1; i <= 8; i++){for (j = 1; j <= 8; j++)printf ("%2d", c (j) == i ? 1 : 0);printf ("\n");}elseprintf ("\n\nNO HAY SOLUCION.\n");}void ensayar (int i){int j = 0;do{j++;

Page 34: Seminario de Sistemas 1 Ing Oralia Zamora FIME

acertado = FALSE;if (f (j) && di (i + j) && dn (i - j)){c (i) = j;f (j) = di (i + j) = dn (i - j) = FALSE;if (i < 8){ensayar (i + 1);if (! acertado)f (j) = di (i + j) = dn (i - j) = TRUE;}elseacertado = TRUE;}} while (! acertado && j != 8);}

Se puede decir que la recursividad es una técnica de programación bastante útil y muy interesante de estudiar.

A través de los ejemplos que el individuo pueda revisar, aprenderá con más rapidez y sencillez lo que es programar recursivamente e incluir esta técnica cuando se le presente un problema como los que fueron mencionados anteriormente.

La asignación de memoria, sea estática o dinámica, en realidad se tendrá que aplicar en cualquier programa al momento de su codificación; tomando en cuenta que cada programador tiene su estilo de programar.

El ejemplo de las torres de Hanoi tanto como el ejemplo de las ocho reinas son problemas claves que tienen que ver directamente con lo que es la recursividad.

Algoritmo recursivo

De Wikipedia, la enciclopedia libreSaltar a: navegación, búsqueda

Se ha sugerido que este artículo o sección sea fusionado con

Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en términos de una llamada a sí mismo.

La llamada a sí mismo se conoce como llamada recursiva o recurrente.

FUNCIÓN Factorial(n) VAR resultado: Entero SI (n<2) ENTONCES

Page 35: Seminario de Sistemas 1 Ing Oralia Zamora FIME

resultado = 1; SINO resultado = n * Factorial(n-1); FSI RETORNA resultado;FFUNCIÓN

Generalmente, si la primera llamada al subprograma se plantea sobre un problema de tamaño u orden N, cada nueva ejecución recurrente del mismo se planteará sobre problemas, de igual naturaleza que el original, pero de un tamaño menor que N.

De esta forma, al ir reduciendo progresivamente la complejidad del problema que resolver, llegará un momento en que su resolución sea más o menos trivial (o, al menos, suficientemente manejable como para resolverlo de forma no recursiva).

En esa situación diremos que estamos ante un caso base de la recursividad.

Las claves para construir un subprograma recurrente son:

Cada llamada recurrente se debería definir sobre un problema de menor complejidad (algo más fácil de resolver).

Ha de existir al menos un caso base para evitar que la recurrencia sea infinita.

Es frecuente que los algoritmos recurrentes sean más ineficientes en tiempo que los iterativos aunque suelen ser mucho más breves en espacio.

Recursividad indirecta

Cuando en una subrutina hay llamadas a ella misma se habla de recursividad directa, en contraposición, cuando se tienen varias subrutinas y estas se llaman unas a otras formando ciclos se dice que la recursión es indirecta.

Subrutina_A → Subrutina_B → Subrutina_ASubrutina_A → Subrutina_B → Subrutina_C → Subrutina_D → Subrutina_A

Aplicaciones

Computación. Informática. Sistemas Expertos. Inteligencia artificial. Prueba de Turing. Búsqueda heurística. Algoritmos de búsqueda. Visión

Page 36: Seminario de Sistemas 1 Ing Oralia Zamora FIME

artificial. Redes neuronales. Robótica. Hardware. Comunicación. Lenguajes. Aplicaciones

Comerciales

Pero también la AI tiene numerosas aplicaciones comerciales en el mundo de hoy. Véase:

• Configuración: selección de distribución de los componentes de un sistema de computación.

Diagnosis: hardware informático, redes de ordenadores, equipos mecánicos, problemas médicos, averías telefónicas, instrumentación electrónica, circuitos electrónicos, averías automovilísticas.

• Interpretación y análisis: datos geológicos para prospección petrolífera, compuestos químicos, análisis de señales, problemas matemáticos complejos, evaluación de amenazas militares, análisis de circuitos electrónicos, datos biológicos (coronarios, cerebrales y respiratorios), información de radar, sonar e infrarrojos.

• Monitorización: equipos, monitorización de procesos, fabricación y gestión de procesos científicos, amenazas militares, funciones vitales de pacientes hospitalizados, datos financieros en tiras de papel perforado por teleimpresora, informes industriales y gubernamentales.

• Planificación: gestión de activo y pasivo, gestión de cartera, análisis de créditos y préstamos, contratos, programación de trabajos de taller, gestión de proyectos, planificación de experimentos, producción de tarjetas de circuito impreso.

• Interfaces inteligentes: hardware (fiscal) de instrumentación, programas de computadora, bases de datos múltiples, paneles de control.

• Sistemas de lenguaje natural: interfaces con bases de datos en lenguaje natural, gestión de impuestos (ayudas para contabilidad), consultoría en temas legales, planificación de fincas, consultoría de sistemas bancarios.

• Sistemas de diseño: integración de microcircuitos en muy alta escala, síntesis de circuitos electrónicos, plantas químicas, edificios, puentes y presas, sistemas de transporte.

• Sistemas de visión computarizada: selección de piezas y componentes, ensamblado, control de calidad • Desarrollo de software: programación automática.

Desafíos técnicos del futuro

Page 37: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Los tres desafíos más importantes en el desarrollo dentro de la A I sosu facilidad de uso, la flexibilidad de la infraestructura computacional y la disponibilidad de herramientas de desarrollo cada vez más poderosas.

El empleo de una interfaz inteligente ayudará a las personas a encontrar lo que ellas deseen, hará lo que éstas deseen cuando lo deseen, en forma natural y sin requerir el conocimiento de detalles irrelevantes.

En fin, todo parece indicar que las computadoras programadas con la A I son el campo de la solución de problemas del futuro.

Sin embargo, el intelecto humano parece ser irremplazable en relación con la solución de problemas de sentido común.

Se sugiere entonces, dado lo complicado de la mente humana, que hombre y máquina interactúen juntos ya que necesitan uno del otro para solucionar eficazmente los problemas.

6. APLICACIONES DE LA INTELIGENCIA ARTIFICIAL Y LAS TÉCNICAS QUE USAN

Dentro del enfoque de la ingeniería de la Inteligencia Artificial, se clasifican las técnicas que pueden ser usadas como herramientas para solucionar problemas en las siguientes categorías:

1. Técnicas básicas, así llamadas por encontrarse a la base de diversas aplicaciones de IA.

Entre otras se encuentran Búsqueda Heurística de Soluciones, Representación del Conocimiento, Deducción Automática, Programación Simbólica (LISP) y Redes Neuronales.

Estas técnicas son las bases de las aplicaciones.

En su mayoría, no necesita conocerla el usuario final, sino los profesionales que se dedican a su aplicación y la generación de aplicaciones comerciales.

2. Tecnologías, o combinaciones de varias técnicas básicas, orientadas a resolver familias de problemas.

Las tecnologías son más especializadas que las técnicas básicas y están más cerca de las aplicaciones finales. Se pueden mencionar a la Robótica y Visión, Lenguaje Natural, Sistemas Expertos

3. Clases o tipos de aplicaciones: Diagnóstico, Predicción (sistemas de autocontrol de reactores atómicos), Secuenciamiento de operaciones ("Scheduling"), Diseño, Interpretación de datos.

Page 38: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Todas ellas son familias de problemas tipo. Por ejemplo, el diagnóstico se refiere a encontrar las causas de fallas, ya sea que se trate de fallas en una línea de producción o de enfermedades en una persona.

4. Campos de aplicación: Ingeniería, Medicina, Sistemas de Manufactura, Administración, Apoyo a la Toma de Decisiones Gerenciales, etc.

Todas caen dentro de las áreas de los sistemas computacionales, pero que se consideran como clientes de la Inteligencia Artificial.

7. APLICACIÓN DE LA INTELIGENCIA ARTIFICIAL EN LOS SISTEMAS PRODUCTIVOS

La incorporación de agentes de decisión inteligente, redes neuronales, sistemas expertos, algoritmos genéticos y autómatas programables para optimización de sistemas de producción es una tendencia activa en el ambiente industrial de países con alto desarrollo tecnológico y con una gran inversión en investigación y desarrollo.

Dichos componentes de la Inteligencia Artificial tienen como función principal controlar de manera independiente, y en coordinación con otros agentes, componentes industriales tales como celdas de manufactura o ensamblaje, y operaciones de mantenimiento, entre otras.

Existe una tendencia creciente a la implementación de sistemas de manufactura/ensamblaje más autónomos e inteligentes, debido a las exigencias del mercado por obtener productos con niveles muy altos de calidad; lo cual con operaciones manuales se hace complicada y hace que los países subdesarrollados como el nuestro no alcance niveles competitivos a nivel mundial.

Al diseñar un sistema de producción integrado por computadora se debe dar importancia a la supervisión, planificación, secuenciación cooperación y ejecución de las tareas de operación en centros de trabajo, agregado al control de los niveles de inventario y características de calidad y confiabilidad del sistema.

Los factores mencionados determinan la estructura del sistema y su coordinación representa una de las funciones más importantes en el manejo y control de la producción.

Muy frecuentemente, la razón para construir un modelo de simulación es para encontrar respuestas a interrogantes tales como ¿Cuáles son los parámetros óptimos para maximizar o minimizar cierta función objetivo?

En los últimos años se han producido grandes avances en el campo de la optimización de sistemas de producción.

Sin embargo, el progreso en el desarrollo de herramientas de análisis para resultados de modelos de simulación ha sido muy lento.

Existe una gran cantidad de técnicas tradicionales de optimización que sólo individuos con gran conocimiento estadístico y de conceptos de simulación han logrado aportes significativos en el área.

Debido al auge de los algoritmos de búsqueda meta-heurísticos, se ha abierto un nuevo campo en el área de optimización con simulación.

Page 39: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Nuevos paquetes de software, tales como OptQuest (Optimal Technologies), SIMRUNNER (Promodel Corporation) y Evolver (Palisade Software), han salido al mercado brindando soluciones amigables de optimización de sistemas que no requieren control interno sobre el modelo construido, sino sobre los resultados que dicho modelo arroja bajo diferentes condiciones.

Además, nuevas técnicas de inteligencia artificial aplicadas a problemas de optimización estocástica, han demostrado su eficiencia y capacidad de cómputo y aproximación.

El Aprendizaje Reforzado (Reinforcement Learning) es un conjunto de técnicas diseñadas para dar solución a problemas cuya base son los procesos de decisión markovianos.

Los procesos markovianos son procesos estocásticos de decisión que se basan en el concepto de que la acción a tomar en un estado determinado, en un instante determinado, depende sólo del estado en que se encuentre el sistema al momento de tomar la decisión.

Una de las áreas que puede tener mayor incidencia directa en los procesos productivos de la industria nivel mundial, es el diseño de sistemas de soporte para la toma de decisiones basados en la optimización de los parámetros de operación del sistema. P

Para tal efecto, el uso de técnicas inteligentes paramétricas y no paramétricas para el análisis de datos es de gran interés.

Sin embargo, a juicio de los autores en la mayoría de las arquitecturas propuestas hasta el momento para manufactura integrada por computadora, carecen de un factor de integración fundamental.

La comunicación entre los diversos niveles jerárquicos de una planta de producción es muy poca, ya que cada departamento se limita a realizar su función sin buscar una integración de toda la planta productiva a excepciones de empresas como ABB con su software Baan, etc.

8. APLICACIONES DE LA INTELIGENCIA ARTIFICIAL EN LA SOLUCION DE PROBLEMAS ESPECIFICOS DE PRODUCCION

Operación automática de control de calidad usando un sistema de visión por computador.

Todo proceso industrial es evaluado por la calidad de su producto final, esto hace de la etapa de control de calidad una fase crucial del proceso.

Los mecanismos utilizados para establecer la calidad de un producto varían dependiendo de los parámetros que tengan relevancia en el mismo.

Cuando el parámetro relevante es la geometría o forma del objeto fabricado se suele dejar a la vista del operario que lleve a cabo tal función tanto de inspección como de verificación para el control de calidad, sin embargo pueden existir errores en la geometría de un objeto que escapen de la vista de un operario y que luego impidan el buen funcionamiento de dicho objeto.

Page 40: Seminario de Sistemas 1 Ing Oralia Zamora FIME

En un caso como éste, surge como una buena alternativa el utilizar un sistema de visión artificial capaz de detectar aquellos errores que un operario pudiera pasar por alto.

El sistema de visión artificial Robot Visión PRO, es capaz de ejecutar de manera totalmente automática las labores de identificación de objetos y de control de calidad de los mismos.

El sistema Robot Visión PRO es un paquete de software de visión que permite la adquisición de imágenes, preprocesamiento y segmentación.

Además realiza procesamiento de datos de alto nivel que brinda filtrado de imágenes, elaboración de clusters y patrones, e identificación de objetos.

Este sistema cuenta con una videocámara y un monitor encargado de identificar cada una de las piezas salientes del proceso y hacer una comparación con piezas de 100% calidad para luego determinar si el empaque puede salir al mercado o debe desecharse.

A continuación se presentan algunas imágenes suministradas por el sistema Robot Visión PRO Para la ejecución de la operación de control de calidad.

Fueron dispuestos los empaques de tal forma que las geometrías quedaran plenamente contenidas en el programa, y se procedió posteriormente a realizar de forma individual el control de calidad para cada uno de los empaques.

Para ver el gráfico seleccione la opción "Descargar" del menú superior

Empaque bueno con 100% de calidad

Se muestra empaques defectuosos porque no cumple con las especificaciones necesarias y por ende el sistema de calidad rechaza el producto.

Para ver el gráfico seleccione la opción "Descargar" del menú superior

<> Empaque rechazado por mala calidad

 Para ver el gráfico seleccione la opción "Descargar" del menú superior

Figura 5. Empaque rechazado por mala calidad

El sistema de visión por computador Robot Visión PRO después de ser evaluado en la empresa resultó eficiente para la detección de defectos geométricos en los empaques de compresores centrífugos, ya que la flexibilidad del software permitió ajustar las condiciones del proceso al sistema de calidad requerido para la apropiada medición de los empaques.

Este sistema es lo bastante didáctico como para desarrollar expresiones que permitan realizar de manera totalmente automática mediciones del objeto, labores de reconocimiento y de control de calidad.

Los autores opinan que es muy adecuado el uso de esta tecnología en empresas donde el acabado superficial de una pieza sea muy exigente ó

Page 41: Seminario de Sistemas 1 Ing Oralia Zamora FIME

estrechas tolerancias como por ejemplo repuestos de carros, instrumentación industrial, etc.

1. JAT (Sistema Inteligente de despacho y Control para el Transporte Publico): su idea principal es mejorar el servicio de transporte urbano de la ciudad de Manizales a través de despacho y control inteligente que permita mejorar la calidad del servicio y reduzca los costos de operación. El parte inteligente se encarga de programar el despacho de rutas buscando todas las busetas las cubran de manera equitativa.

Sistema inteligente de Vigilancia y Monitoreo Remoto: se busca implementar sistemas de circuito cerrado de TV, que incluyan la capacidad de monitoreo remoto a través de un computador y una línea telefónica desde cualquier lugar del mundo y a través de Internet.

2. Proyectos en vía de desarrollo por la línea de investigación y desarrollo de inteligencia artificial (grupo de investigación de la Universidad de Manizales)

Este estudio está centrado en la identificación global de ambientes ejecutada por un robot móvil con base en el entrenamiento de una red neuronal que recibe la información captada del medio ambiente por el sistema sensorial del robot (ultrasonido).

Se considera que el robot, a través de la red neuronal, tiene como única tares maximizar el conocimiento del ambiente que se le presenta. De esta forma este modela y explora el ambiente eficientemente mientras ejecuta algoritmos de evasión de obstáculos.

El resultado de este estudio es de gran importancia en el campo de la robótica móvil debido a que: el robot adquiere una mayor autonomía del movimiento, se optimiza el uso del ultrasonido como detector de obstáculos y es una herramienta importante para el desarrollo de planificadores de trayectoria y controladores ´´inteligentes´´.

Usando una arquitectura: 2 - 2 -1

Nih: Número de neuronas de entrada(2).

Nhid: Número de neuronas de la capa intermedia(1).

Nout: Número de neuronas de salida(2).

Se va a mostrar a groso modo uno de los ejemplos con los cuáles fue entrenada la red (para mayor detalle consultar investigación de Rivera & Gauthier [1995] Universidad de los Andes).

Los parámetros usados en el entrenamiento fueron constante de aprendizaje de 0.2 y constante de momento de 0.9

Para ver el gráfico seleccione la opción "Descargar" del menú superior

Page 42: Seminario de Sistemas 1 Ing Oralia Zamora FIME

Ambiente de entrenamiento de tres obstáculos

Se ubica el robot en ocho posiciones diferentes y en cada una de estas se hizo un barrido y de esta manera se formaron ocho archivos con los cuales se entreno la red, y esta ya reconociendo el ambiente no se estrellará con ningún obstáculo.

En la red neuronal a medida que se aumenten las capaz internas tendrá más capacidad y velocidad de aprender diversos ambientes.

A intervención de los autores, determinan que es muy importante el uso de la robótica móvil en procesos productivos donde el hombre no pueda soportar ambientes de altas temperaturas o bajas temperaturas por intervalos largos de tiempo, como por ejemplo en MEALS, donde se podría entrenar un robot y a medida que se perfeccione su entrenamiento prepararlo posteriormente como transportador de carga.

3. Reconocimiento de ambientes en robótica móvil por medio de redes neuronales

4. Algoritmos genéticos aplicados al problema cuadrático de asignación de facilidades QAP (Departamento de Investigación Operativa, Escuela de Ingeniería Industrial, Universidad de Carabobo, Valencia, Venezuela. Ninoska Maneiro. Algoritmo Genético Aplicado a Problemas de Localización de Facilidades. Año 2001 www.cemisid.ing.ula.ve/area3 ).

El QAP es un problema combinatorio, considerado por algunos autores como NP-completo. El objetivo del QAP es encontrar una asignación de facilidades a sitios, a fin de minimizar una función que expresa costos o distancias.

La localización y distribución de facilidades es uno de los tópicos más importantes en la formación de profesionales en el área de Ingeniería Industrial y de todos aquellos

profesionales que se encargan de la planificación, organización y crecimiento sistemático de las ciudades. En la vida cotidiana y profesional de todo individuo, se presentan una gran variedad de problemas de localización de facilidades.

Los problemas de localización y distribución de facilidades son estratégicos para el éxito de cualquier operación de manufactura.

La principal razón es que los costos de manejo al de materiales comprenden entre el 30 y el 75% de los costos totales de manufactura.

Una buena solución problema de asignación de facilidades contribuiría a la eficiencia total de las operaciones, una pobre distribución puede conducir a la acumulación de inventario de producto en proceso, sobrecarga de los sistemas de manejo de materiales, puestas a punto ineficientes y largas colas.

Dentro de esta amplia clase de problemas que pueden ser catalogados como QAP se encuentra el problema de flujo en línea generalizado, que es

Page 43: Seminario de Sistemas 1 Ing Oralia Zamora FIME

una línea de flujo en la cual las operaciones fluyen hacia adelante y no se procesan necesariamente en todas las máquinas de la línea.

Un trabajo en tal clase de línea puede comenzar a procesarse y completar su proceso en cualquier máquina, moviéndose siempre hacia delante (downstream) por operaciones sucesivas de acuerdo con la secuencia de trabajo del proceso.

Cuando la secuencia de operaciones para un trabajo no especifica una máquina colocada delante de su localización actual, el trabajo tiene que viajar en sentido contrario (upstream) a fin de completar la operación requerida.

Este <>"viaje en reversa" de las operaciones, es llamado backtracking, y se desvía de una línea de flujo ideal para un trabajo específico, resultando en una estructura de trabajo menos eficiente, como se muestra en la siguiente figura.

Al parecer de los autores, este problema de asignación cuadrática debería tratarse en la clase de taller de producción por su relevancia al analizar secuencias N / M.

Una línea de flujo generalizada Fuente: Ninoska Maneiro 2001.

9. CONCLUSIONES

En la universidad Nacional sede Manizales en el programa de ingeniería industrial se debería trabajar más en las ciencias informáticas, con el fin, de profundizar en áreas de la inteligencia artificial aplicadas a la ingeniería industrial.

Con el desarrollo de este trabajo se han obtenido resultados satisfactorios a nivel de investigación teórica, ya que con la documentación obtenida se conocieron avances en las ciencias informáticas que en algunos casos eran desconocidos para los autores.

Los grandes avances de I.A aplicada a sistemas de producción han hecho que día a día la industria en su constante búsqueda por mejorar su competitividad logren dicho objetivo, pero en muchos de los casos desplazar gran cantidad de mano de obra que llevan consigo un deterioro social que se ve reflejado en los indicadores globales de desempleo y niveles de pobreza.