Está en la página 1de 44

rboles y Busquedas

Un rbol de decisin es un modelo de prediccin utilizado en el mbito de la inteligencia artificial. Dada una base de datos se construyen diagramas de construcciones lgicas, muy similares a los sistemas de prediccin basados en reglas, que sirven para representar y categorizar una serie de condiciones que ocurren de forma sucesiva, para la resolucin de un problema. Un rbol de decisin tiene unas entradas las cuales pueden ser un objeto o una situacin descrita por medio de un conjunto de atributos y a partir de esto devuelve una respuesta la cual en ltimas es una decisin 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 ms los valores discretos por simplicidad, cuando se utilizan valores discretos en las funciones de una aplicacin se denomina clasificacin y cuando se utilizan los continuos se denomina regresin. Lleva a cabo un test a medida que este se recorre hacia las hojas para alcanzar as una decisin. Contener nodos internos, nodos de probabilidad, nodos hojas y arcos. Un nodo interno contiene un test sobre algn 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 dems son cuadrados. Un nodo hoja representa el valor que devolver el rbol de decisin y finalmente las ramas brindan los posibles caminos que se tienen de acuerdo a la decisin tomada. De forma ms concreta, refirindonos al mbito empresarial, podemos decir que los rboles de decisin 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 opcin que evita una prdida o produce un beneficio extra tiene un valor.

La habilidad de crear un opcin, 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 bsqueda de hiptesis 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, vlido o invlido, etc. ). De esta forma el algoritmo trata de obtener las hiptesis que clasifiquen ante nuevas instancias, si dicho ejemplo va a ser positivo o negativo. ID3 realiza esta labor mediante la construccin de un rbol de decisin. Los elementos son:

Nodos: Los cuales contendrn 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 vaco 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 vaco devolver un nodo con el voto mayoritario del Atributo objetivo de Ejemplos Sino Devolver Id3(Ejemplos(v), Atributo-objetivo, Atributos/{A})

Obsrvese que la construccin del rbol se hace forma recursiva, siendo las tres primeras lneas y la penltima los casos base que construyen los nodos hojas. Eleccin del mejor atributo La eleccin del mejor atributo se establece mediante la entropa. Eligiendo aquel que proporcione una mejor ganancia de informacin. La funcin elegida puede variar, pero en su forma ms 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 Temperatu ra Humeda d Vient o Jugar tenis

D1

Sol

Alta

Alta

Dbil

D2

Sol

Alta

Alta

Fuer te

D3

Nub es

Alta

Alta

Dbil

D4

Lluvi a

Suave

Alta

Dbil

D5

Lluvi a

Baja

Normal

Dbil

D6

Lluvi a

Baja

Normal

Fuer te

D7

Nub es

Baja

Normal

Fuer te

D8

Sol

Suave

Alta

Dbil

D9

Sol

Baja

Normal

Dbil

D10

Lluvi a

Suave

Normal

Dbil

D11 Sol

Suave

Normal

Fuer te

D12

Nub es

Suave

Alta

Fuer te

D13

Nub es

Alta

Normal

Dbil

D14

Lluvi a

Suave

Alta

Fuer te

En ese caso el rbol finalmente obtenido seri as: Cielo / | \ / | \ Soleado / Nublado \ Lluvia / | \ / + Humedad Viento

/ \ | \ / \ | \ Alta/ \ Normal Fuerte | / \ | \ + +

\ Dbil

La programacin lgica Consiste en la aplicacin del corpus de conocimiento sobre lgica para el diseo de lenguajes de programacin. Comprende dos paradigmas de programacin: Declarativa funcional. La programacin declarativa gira en torno al concepto depredicado, o relacin entre elementos. La programacin funcional se basa en el concepto defuncin (que no es ms que una evolucin de los predicados), de corte ms temtico. Es un tipo de paradigmas de programacin dentro del paradigma de programacin declarativa. El resto de los subparadigmas de programacin dentro de la programacin declarativa son: programacin funcional, programacin basada en restricciones, programas DSL (de dominio especfico) e hbridos. La programacin lgica gira en torno al concepto de predicado, o relacin entre elementos. La programacin funcional se basa en el concepto de funcin (que no es ms que una evolucin de los predicados), de corte ms matemtico. Histricamente, los ordenadores se han programado utilizando lenguajes muy cercanos a las peculiaridades de la propia mquina: operaciones aritmticas simples, instrucciones de acceso a memoria, etc. Un programa escrito de esta manera puede ocultar totalmente su propsito a la comprensin de un ser humano, incluso uno entrenado. Hoy da, estos lenguajes pertenecientes al paradigma de la Programacin imperativa han evolucionado de manera que ya no son tan crpticos Campos de aplicacin

La programacin lgica encuentra su hbitat natural en aplicaciones de inteligencia artificial o relacionadas: Sistemas expertos, donde un sistema de informacin imita las recomendaciones de un experto sobre algn dominio de conocimiento. Demostracin automtica de teoremas, donde un programa genera nuevos teoremas sobre una teora existente. Reconocimiento de lenguaje natural, donde un programa es capaz de comprender (con limitaciones) la informacin contenida en una expresin lingstica humana, etc. La programacin lgica tambin se utiliza en aplicaciones ms "mundanas" pero de manera muy limitada, ya que la programacin tradicional es ms adecuada a tareas de propsito general. Fundamentos La mayora de los lenguajes de programacin lgica se basan en la teora lgica de primer orden, aunque tambin incorporan algunos comportamientos de orden superior. En este sentido, destacan los lenguajes funcionales, ya que se basan en el clculo lambda, que es la nica teora lgica de orden superior que es demostradamente computable (hasta el momento). Lenguajes El lenguaje de programacin lgica por excelencia esProlog, que cuenta con diversas variantes. La ms importante es la programacin lgica con restricciones, que posibilita la resolucin deecuac iones lineales adems de la demostracin de hiptesis.

Demostracin automtica de teoremas, donde un programa genera nuevos teoremas sobre una teora existente. Reconocimiento de lenguaje natural, donde un programa es capaz de comprender (con limitaciones) la informacin contenida en una expresin lingstica humana, etc.

La programacin lgica tambin se utiliza en aplicaciones ms "mundanas" pero de manera muy limitada, ya que la programacin tradicional es ms adecuada a tareas de propsito general.

Recursividad

Anuncio de cacao con una imagen recursiva. La mujer muestra un paquete idntico al del propio anuncio, conteniendo as a otra mujer que muestra otro paquete ms pequeo, de forma recursiva.

Imagen recursiva formada por un tringulo. Cada tringulo est compuesto de otros ms pequeos, compuestos a su vez de la misma estructura recursiva. Recurrencia, recursin o recursividad es la forma en la cual se especifica un proceso basado en su propia definicin. Siendo un poco ms precisos, y para evitar el aparente crculo sin fin en esta definicin: Un problema que pueda ser definido en funcin de su tamao, sea este N, pueda ser dividido en instancias ms pequeas (< N) del mismo problema y se conozca la solucin explcita a las instancias ms simples, lo que se conoce como casos base, se puede aplicar induccin sobre las llamadas ms pequeas y suponer que estas quedan resueltas. Para que se entienda mejor a continuacin se exponen algunos ejemplos:

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

Ordenacin por fusin(v: vector): Sea N := tamao(v), podemos separar el vector en dos mitades. Estas dos mitades tienen tamao N/2 por lo que por induccin podemos aplicar la ordenacin 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 tamao menor gracias a lo cual se puede aplicar induccin, llegando a un punto donde se conoce el resultado (el caso base).. Nota: aunque los trminos "recursin" y "recursividad" son ampliamente empleados en el campo de la informtica, el trmino correcto en castellano es recurrencia. Sin embargo este ltimo trmino es algo ms especfico. Los nmeros naturales Un ejemplo de conjunto definido de forma recurrente es el de los nmeros naturales: a) 0 pertenece a N b) Si n pertenece a N, entonces n+1 pertenece a N c) Si X verifica a) y b) , entonces N est incluido en X Los nmeros naturales es el conjunto de nmeros enteros no negativos. Funciones definidas de forma recurrente Aquellas funciones cuyo dominio puede ser recursivamente definido pueden ser definidas de forma recurrente. El ejemplo ms conocido es la definicin recurrente de la funcin factorial n!:

Con esta definicin veamos cmo funciona esta funcin 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! =3211 =6

Algoritmo recursivo Un mtodo usual de simplificacin de un problema complejo es la divisin de este en subproblemas del mismo tipo. Esta tcnica de programacin se conoce como divide y vencers y es el ncleo en el diseo de numerosos algoritmos de gran importancia, as como tambin es parte fundamental de la programacin dinmica. El ejemplo del clculo recursivo del factorial de un nmero llevado al campo de la programacin, 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 nmeros 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 acompaado 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 3 X >= 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 atrs 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 Resolucin de ecuaciones homogneas de primer grado, segundo orden: a) Se pasan al primer miembro los trminos an, an tambin podran figurar como an + 2, an + 1, an
1

, an

, los cuales

b) Se reemplaza an por r2, an 1 por r y an 2 por 1, quedando una ecuacin de segundo grado con races reales y distintas r1 y r2. c) Se plantea d) Debemos tener como dato los valores de los dos primeros trminos de la sucesin: sistema de 2x2: y . Utilizando estos datos ordenamos el

La resolucin de este sistema nos da como resultado los valores u0 y v0, que son nmeros reales conocidos. e) La solucin general es:

El rea de la programacin es muy amplia y con muchos detalles. Los programadores necesitan ser capaces de resolver todos los problemas que se les presente a travs del computador aun cuando en el lenguaje que utilizan no haya una manera directa de resolver los problemas. En el lenguaje de programacin C, as como en otros lenguajes de programacin, se puede aplicar una tcnica que se le dio el nombre de recursividad por su funcionalidad. Esta tcnica es utilizada en la programacin estructurada para resolver problemas que tengan que ver con el factorial de un nmero, o juegos de lgica. Las asignaciones de memoria pueden ser dinmicas o estticas y hay diferencias entre estas dos y se pueden aplicar las dos en un programa cualquiera.

Recursividad: La recursividad es una tcnica de programacin importante. Se utiliza para realizar una llamada a una funcin desde la misma funcin. Como ejemplo til se puede presentar el clculo de nmeros factoriales. l factorial de 0 es, por definicin, 1. Los factoriales de nmeros mayores se calculan mediante la multiplicacin de 1 * 2 * ..., incrementando el nmero de 1 en 1 hasta llegar al nmero para el que se est calculando el factorial. El siguiente prrafo muestra una funcin, expresada con palabras, que calcula un factorial. "Si el nmero es menor que cero, se rechaza. Si no es un entero, se redondea al siguiente entero. Si el nmero es cero, su factorial es uno. Si el nmero es mayor que cero, se multiplica por l factorial del nmero menor inmediato." Para calcular el factorial de cualquier nmero mayor que cero hay que calcular como mnimo el factorial de otro nmero. La funcin que se utiliza es la funcin en la que se encuentra en estos momentos, esta funcin debe llamarse a s misma para el nmero menor inmediato, para poder ejecutarse en el nmero actual. Esto es un ejemplo de recursividad. La recursividad y la iteracin (ejecucin en bucle) estn muy relacionadas, cualquier accin que pueda realizarse con la recursividad puede realizarse con iteracin y viceversa. Normalmente, un clculo determinado se prestar a una tcnica u otra, slo necesita elegir el enfoque ms natural o con el que se sienta ms cmodo. Claramente, esta tcnica puede constituir un modo de meterse en problemas. Es fcil crear una funcin recursiva que no llegue a devolver nunca un resultado definitivo y no pueda llegar a un punto de finalizacin. 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 recursin veamos un poco lo referente a la secuencia de Fibonacci.

Principalmente habra 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 definicin recursiva (define un objeto en trminos de un caso mas simple de si mismo): fib (n) = n if n = = 0 or n = = 1 fib (n) = fib (n - 2) + fib (n - 1) if n >= 2 Por ejemplo, para calcular fib (6), puede aplicarse la definicin de manera recursiva para obtener: 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 Obsrvese que la definicin recursiva de los nmeros de Fibonacci difiere de las definiciones recursivas de la funcin factorial y de la multiplicacin. La definicin 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) tambin implica calcular fib (4), as que al aplicar la definicin hay mucha redundancia de clculo. En ejemplo anterior, fib(3) se calcula tres veces por separado. Sera 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 mtodo 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) ; Comprese 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 definicin recursiva. En el caso de la funcin factorial, tienen que ejecutarse el mismo numero de multiplicaciones para calcular n!. Mediante ambos mtodos: recursivo e iterativo. Lo mismo ocurre con el numero de sumas en los dos mtodos al calcular la multiplicacin. Sin embargo, en el caso de los nmeros de Fibonacci, el mtodo 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 funcin recursiva f debe definirse en trminos 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 funcin recursiva. Cualquier caso de definicin recursiva o invocacin de un algoritmo recursivo tiene que reducirse a la larga a alguna manipulacin de uno o casos mas simples no recursivos. 3.- Cadenas recursivas: Una funcin 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 funcin 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 slo a a. Pueden incluirse mas de dos rutinas en una cadena recursiva. 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.- Definicin recursiva de expresiones algebraicas: Como ejemplo de cadena recursiva consideremos el siguiente grupo de definiciones: una expresin es un trmino seguido por un signo mas seguido por un trmino, o un trmino solo un trmino es un factor seguido por un asterisco seguido por un factor, o un factor solo. Un factor es una letra o una expresin encerrada entre parntesis. Antes de ver algunos ejemplos, obsrvese que ninguno de los tres elementos anteriores est definido en forma directa en sus propios trminos. Sin embargo, cada uno de ellos se define de manera indirecta. Una expresin se define por medio de un trmino, un trmino por medio de un factor y un factor por medio de una expresin. De manera similar, se define un factor por medio de una expresin, que se define por medio de un trmino 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. Tambin son trminos, dado que un trmino puede ser un factor slo. Tambin son expresiones dado que una expresin puede ser un trmino slo. Como A es una expresin, (A) es un factor y, por lo tanto, un trmino y una expresin. A + B es un ejemplo de una expresin que no es ni un trmino ni un factor. Sin embargo (A + B) es las tres cosas. A * B es un trmino y, en consecuencia, una expresin, pero no es un factor. A * B + C es una expresin, pero no es un factor. Cada uno de los ejemplos anteriores es una expresin valida. Esto puede mostrarse al aplicar la definicin de una expresin de cada uno. Considrese, sin embargo la cadena A + * B. No es ni una expresin, ni un trmino, ni un factor. Sera instructivo para el lector intentar aplicar la definicin de expresin, trmino 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 continuacin se codificar un programa que lea e imprima una cadena de caracteres y luego imprima "valida" si la expresin lo es y "no valida" de no serlo. Se usan tres funciones para reconocer expresiones, trminos y factores, respectivamente. Primero, sin embrago se presenta una funcin auxiliar getsymb que opera con tres parmetros: str, length y ppos. Str contiene la entrada de la cadena de cadena de caracteres; length representa el nmero de caracteres en str. Ppos apunta a un puntero pos cuyo valor es la posicin str de la que obtuvimos un carcter la ultima vez. Si pos < length, getsymb regresa el carcter 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]; else c=; (*ppos) ++; return ( c ); } /* fin de getsymb*/ La funcin que reconoce una expresin se llama expr. Regresa TRUE (o1) (VERDADERO) si una expresin valida comienza en la posicin pos de str y FALSE (o0) FALSO en caso contrario. Tambin vuelve a colocar pos en la posicin que sigue en la expresin de mayor longitud que puede encontrar. Suponemos tambin una funcin 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 estndar ctype.h incluye una funcin isalpha que es llamada por una de las funciones siguientes. # include <stdio.h> # include <ctype.h> # define TRUE 1 # define FALSE = # define MAXSTRINGSIZE 100 main () { char str [MAXSTRINGSIZE]; int length, pos; readstr (str, &length); pos = 0; if (expr (str, length, &pos) = = TRUE && por >= length) printf ("%s", "valida"); else printf ("%s", "no valida"); /* La condicin puede fallar por una de dos razones (o ambas). Si expr(str, length, &pos) = = FALSE entonces no hay una expresin valida al inicio de pos. Si pos < length puede que se encuentre una expresin 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 trmino, respectivamente. Tambin reinicializan pos en la posicin que sigue al factor o trmino de mayor longitud que se encuentra en la cadena str. Los cdigos para estas rutinas se apegan bastantes a las definiciones dadas antes. 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 trmino */ if (term( str, length, ppos) = = FLASE) return (FLASE); /* se ha encontrado un trmino; revisar el siguiente smbolo */ if (getsymb(str, length, ppos) ! = +) { /* se encontr la mayor expresin (un solo trmino). Reposicionar pos para que seale la ltima posicin de la expresin */ (*ppos) - - ; return (TRUE); } /* fin del if */ /* En este punto, se a encontrado un termino y su signo mas. Se deber buscar otro trmino */ return (term(str, length, ppos)); } /*fin de expr */ La rutina term que reconoce trminos, 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 funcin factor reconoce factores y debera ser ahora bastante sencilla. Usa el programa comn de biblioteca isalpha (esta funcin se encuentra en la biblioteca ctype.h), que regresa al destino de cero si su carcter de parmetro es una letra y cero (o FALSO) en caso contrario. 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 accin 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.- Programacin Recursiva: Es mucho mas difcil desarrollar una solucin 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 razn para buscar una solucin recursiva. La mayora de los problemas pueden resolverse de una manera directa usando mtodos no recursivos. Sin embargo, otros pueden resolverse de una manera mas lgica y elegante mediante la recursin. Volviendo a examinar la funcin factorial.

El factor es, probablemente, un ejemplo fundamental de un problema que no debe resolverse de manera recursiva, dado que su solucin iterativa es directa y simple. Sin embargo, examinaremos los elementos que permiten dar una solucin recursiva. Antes que nada, puede reconocerse un gran nmero de casos distintos que se deben resolver. Es decir, quiere escribirse un programa para calcular 0!, 1!, 2! Y as sucesivamente. Puede identificarse un caso "trivial" para el cual la solucin no recursiva pueda obtenerse en forma directa. Es el caso de 0!, que se define como 1. El siguiente paso es encontrar un mtodo para resolver un caso "complejo" en trminos de uno mas "simple", lo cual permite la reduccin de un problema complejo a uno mas simple. La transformacin del caso complejo al simple resultara al final en el caso trivial. Esto significara que el caso complejo se define, en lo fundamental, en trminos del mas simple. Examinaremos que significa lo anterior cuando se aplica la funcin factorial. 4! Es un caso mas complejo que 3!. La transformacin 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 trminos de 3! y, en general, n! en trminos de (n 1)!, se podr calcular 4! mediante la definicin de n! en trminos de (n 1)! al trabajar, primero hasta llegar a 0! y luego al regresar a 4!. En el caso de la funcin factorial se tiene una definicin 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 trminos de uno ms "simple" y tener un caso "trivial" (no recursivo) que pueda resolverse de manera directa.

Al hacerlo, puede desarrollarse una solucin si se supone que se ha resuelto el caso ms simple. La versin C de la funcin 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 definicin de a * b, es trivial el caso de b = 1, pues a * b es igual a a. En general, a + b puede definirse en trminos de a * (b 1) mediante la definicin a * b = a * (b 1) + a. 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 recursin se basa nicamente en el segundo parmetro, b. Con respecto al ejemplo de la funcin de Fibonacci, se definieron dos casos triviales: fib(0) = 0 y fib(1) = 1. Un caso complejo fib(n) se reduce entonces a dos ms simples: fib(n 1) y fib(n 2). Esto se debe a la definicin 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 funcin de Fibonacci no est definida para nmeros negativos. 6.- Asignacin esttica y dinmica de memoria: Hasta este momento solamente hemos realizado asignaciones estticas del programa, y ms concretamente estas asignaciones estticas no eran otras que las declaraciones de variables en nuestro programa. Cuando declaramos una variable se reserva la memoria suficiente para contener la informacin que debe almacenar. Esta memoria permanece asignada a la variable hasta que termine la ejecucin del programa (funcin 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 cdigo para esta operacin automticamente. En este sentido las variables locales estn asociadas a asignaciones de memoria dinmicas, puesto que se crean y destruyen durante la ejecucin del programa. As entendemos por asignaciones de memoria dinmica, aquellas que son creadas por nuestro programa mientras se estn ejecutando y que por tanto, cuya gestin debe ser realizada por el programador.

El lenguaje C dispone, como ya indicamos con anterioridad, de una serie de libreras de funciones estndar. El fichero de cabeceras stdlib.h contiene las declaraciones de dos funciones que nos permiten reservar memoria, as como otra funcin que nos permite liberarla. Las dos funciones que nos permiten reservar memoria son: malloc (cantidad_de_memoria); calloc (nmero_de_elementos, tamao_de_cada_elemento); Estas dos funciones reservan la memoria especificada y nos devuelven un puntero a la zona en cuestin. Si no se ha podido reservar el tamao 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 ests funciones es aconsejable realizar una operacin cast (de conversin de tipo) de cara a la utilizacin de la aritmtica de punteros a la que aludamos anteriormente. Los compiladores automticamente. modernos suelen realizar esta conversin

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 mquina que disponga de un compilador de C. El operador sizeof (tipo_de_dato), nos devuelve el tamao que ocupa en memoria un cierto tipo de dato, de esta manera, podemos escribir programas independientes del tamao de los datos y de la longitud de palabra de la mquina. En resumen si no utilizamos este operador en conjuncin con las conversiones de tipo cast probablemente nuestro programa slo 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 mquina est orientada a palabras (conjuntos de 2 bytes. Aunque en general una mquina orientada a palabras tambin puede acceder a bytes) y por tanto el tamao de un entero sera de 1 posicin de

memoria, suponiendo que ambas mquinas definan la misma precisin para este tipo. Con todo lo mencionado anteriormente veamos un ejemplo de un programa que reserva dinmicamente memoria para algn dato. #include <stdlib.h #include <stdio.h> main() { int *p_int; float *mat; 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 iran las operaciones sobre los datos */ /* Aqu ira el cdigo 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. Obsrvese el uso de los operadores cast para modificar el tipo del puntero devuelto por malloc y calloc, as como la utilizacin del operador sizeof. Como se puede observar no resulta rentable la declaracin de una variable simple (un entero, por ejemplo, como en el programa anterior) dinmicamente. En primer lugar por que aunque la variable slo se utilice en una pequea parte del programa, compensa tener menos memoria (2 bytes para un entero) que incluir todo el cdigo de llamada a malloc y comprobacin de que la asignacin fue correcta (esto seguro que ocupa ms de dos bytes). En segundo lugar tenemos que trabajar con un puntero con lo cual el programa ya aparece un poco ms 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. Bsicamente 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 dinmicas calloc es mucho ms claro y adems inicializa todos los elementos de la matriz a cero. Ntese tambin que puesto que las matrices se referencian como un puntero la asignacin dinmica de una matriz nos permite acceder a sus elementos con instrucciones de la forma: NOTA: En realidad existen algunas diferencias al trabajar sobre mquinas con alineamiento de palabras. 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 dinmicas no existe diferencia alguna con una declaracin normal de matrices. La funcin 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 cdigo etiquetado como: /* Ahora ira el cdigo 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 funcin free probablemente no podr liberar el bloque de memoria. 7.- Ejemplos: 7.1.- Las Torres de Hanoi:

A continuacin se ver cmo pueden usarse tcnicas recursivas para lograr una solucin lgica y elegante de un problema que no se especifica en trminos recursivos. EL problema es el de "las torres de Hanoi", cuyo planteamiento inicial se muestra en la figura a continuacin... Hay tres postes: A, B y C. En el poste A se ponen cinco discos de dimetro diferente de tal manera que un disco de dimetro mayor siempre queda debajo de uno de dimetro menor. El objetivo es mover los discos al poste C usando B como auxiliar. Slo puede moverse el disco superior de cualquier poste a otro poste, y un disco mayor jams puede quedar sobre uno menor. Considrese la posibilidad de encontrar una solucin. En efecto, ni siquiera es claro que exista una. Ahora se ver si se puede desarrollar una solucin. En lugar de concentrar la atencin en una solucin para cinco discos, considrese el caso general de n discos. Supngase que se tiene una solucin para n 1 discos y que en trminos de sta, se pueda plantear la solucin para n 1 discos. El problema se resolvera 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 solucin es simple: slo hay que el nico disco del poste A a C. As se habr desarrollado una solucin recursiva si se plantea una solucin para n discos en trminos de n 1. Considrese la posibilidad de encontrar tal relacin. Para el caso de cinco discos en particular, supngase que se conoce la forma de mover cuatro de ellos del poste A al otro, de acuerdo con las reglas. Cmo puede completarse entonces el trabajo de mover el quinto disco? Cabe recordar que hay 3 postes disponibles. Supngase que se supo cmo 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 situacin 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 solucin recursiva para cuatro discos para moverlo de B a C, usando el poste A como auxilia. Por lo tanto, se puede establecer una solucin 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 auxiliar Con toda seguridad este algoritmo producir una solucin completa por cualquier valor de n. Si n = = , el paso 1 ser la solucin correcta. Si n = = 2, se sabe entonces que hay una solucin para n 1 = = 1, de manera tal que los pasos 2 y 4 se ejecutaran en forma correcta. De manera anloga,

cuando n = = 3 ya se habr producido una solucin para n 1 = = 2, por lo que los pasos 2 y 4 pueden ser ejecutados. De esta forma se puede mostrar que la solucin funciona para n = = 1, 2, 3, 4, 5,... hasta el valor para el que se desee encontrar una solucin. Advirtase que la solucin se desarrollo mediante la identificacin de un caso trivial (n = = 1) y una solucin para el caso general y complejo (n) en trminos de un caso mas simple (n 1). Ya se demostr que las transformaciones sucesivas de una simulacin no recursivas de una rutina recursiva pueden conducir a un programa mas simple para resolver un problema. Ahora se simulara la recursin del problema y se intentara simplificar la simulacin no recursiva. towers (n, frompeg, topeg, auxpeg) 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 funcin, hay cuatro parmetros, 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 simulacin y no tiene que estar apilada. Hay tres puntos posibles a los que regresa la funcin 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 direccin de regreso se codifica como un entero (1, 2 o 3) dentro de cada rea de datos. Considrese la siguiente simulacin 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) 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 parmetros 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; 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 sealarse por un subdesborde en la pila, de la misma for que en la segunda versin simfact. Esto deja dos etiquetas de regreso. Si pudiera eliminarse otra mas, sera innecesario guardar en la pila la direccin de regreso, ya que solo restara un punto al que se podra transferir el control si se eliminan los elementos de la pila con xito. Ahora dirijamos nuestra atencin a la segunda llamada recursiva y a la instruccin: towers (n 1, auxpeg, topeg, frompeg); Las acciones que ocurren en la simulacin 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 parmetros.

En el rea de datos vigente a 2, se fija la etiqueta de retorno a la direccin de la instruccin que sigue de inmediato a la llamada. Se salta hacia el principio de la rutina simulada. Despus 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. 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 instruccin de la funcin. Por lo tanto, el siguiente paso es volver a eliminar elementos de la pila y regresar. No se volver a hacer uso de la informacin del rea de datos vigente a 1, ya que sta es destruida en la eliminacin de los elementos en la pila tan pronto como se vuelve a almacenar. Puesto que no hay razn para volver a usar esta rea de datos, tampoco hay razn para salvarla en la pila durante la simulacin de la llamada. Los datos se deben salvar en la pila slo si se van a usar otra vez. En consecuencia, la seguida llamada a towers puede simularse en forma simple mediante: El cambio de los parmetros 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 versin vigente. No hay razn para ejecutar un regreso a la versin vigente, slo para regresar de inmediato a la versin previa. Por lo tanto, se elimina la necesidad de guardar en la pila la direccin de regreso al simular la llamada externa (ya que se puede sealar 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 direccin de regreso que resta es la que sigue a la primera llamada recursiva. Ya que slo queda una direccin 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 direccin hacia la que se puede ejecutar un "salto" (la instruccin que sigue a la primera llamada). Como los nuevos valores de las variables del rea de datos vigente se obtendrn 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 lnea horizontal, vertical o diagonal, ver Figura 1. Para ver el grfico seleccione la opcin "Descargar" del men superior Figura 1 Posible solucin para el problema de las ocho reinas Este programa has sido muy estudiado por los matemticos. Se trata de un problema NP-Completo que no tiene solucin para N=2 y N=3. Para N=4 tiene una nica solucin. Para N=8 hay ms de 80 soluciones dependiendo de las restricciones que se impongan. Una forma de abordar el problema se lleva a cabo mediante la construccin de un predicado en Prolog del tipo siguiente: reinas (N, Solucin), donde N representa las dimensiones del tablero y el nmero de reinas y Solucin es una lista que contiene la permutacin de la lista de nmeros que resuelven el problema. Los ndices de dicha lista representan la columna en la que se encuentra una reina y el nmero que almacena la posicin 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 clsico, por el mtodo de prueba y error, luego se adapta perfectamente a un algoritmo de backtracking. Bsicamente, 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 combinacin. Por tanto, hemos de generar un conjunto de permutaciones y seleccionar aquella que cumpla los requisitos impuestos por el juego. Veamos el cdigo que resuelve el problema: rango(N, N, [N]). 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 anlisis y diseo de la solucin desde un alto nivel de abstraccin. Adems el procedimiento de backtracking est implcito en el propio motor de inferencia, luego este paradigma se adapta perfectamente a nuestro problema. Si analizamos y diseamos nuestro problema tenemos que la forma de resolverlo se resume en los pasos siguientes: Para N, obtenemos una lista de nmeros comprendidos entre 1 y N: [1,2,3,4,...,N]. Obtenemos una permutacin del conjunto de nmeros de la lista. Comprobamos que la permutacin es correcta. Si la permutacin no es correcta, lo que debemos hacer es volver al paso 2 para generar una permutacin nueva. Comencemos a analizar la solucin implementada. El problema se resuelve con el predicado reina(N, Solucin): rango(1,N,L1), permuta(L1,Solucion), correcta Solucin).

Como vemos es, sencillamente, una copia de las especificaciones realizadas ms arriba. Se genera el rango entre 1 y N, se obtiene una permutacin y se comprueba si la permutacin es, o no, correcta. En el caso de que cualquier predicado del consecuente falle, la propia mquina Prolog se encarga de realizar el proceso de backtracking. Con lo cual ya tenemos cubiertos los cuatro pasos fundamentales del algoritmo. Para tener ms claras las ideas, observemos el rbol de ejecucin general del objetivo reina(4,Solucin) en la Figura 2.

Figura 2 rbol de ejecucin para el objetivo reina(4,Solucin) He aqu otro ejemplo sobre el problema de las ocho reinas. Primero se mostrara un pseudocdigo sobre el mismo y luego su respectiva codificacin en el lenguaje C. <PRINCIPIO ensayar> (i: entero) inicializar el conjunto de posiciones de la reina i-sima +-REPETIR hacer la seleccin 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 ms posiciones <FIN>

Observaciones sobre el cdigo: 1) Estudiar la funcin ensayar() a partir de este pseudocdigo. 2) Vectores utilizados: int posiciones_en_columna[8]; RANGO: 1..8 BOOLEAN reina_en_fila[8]; RANGO: 1..8 BOOLEAN reina_en_diagonal_normal[15]; RANGO: -7..7 BOOLEAN reina_en_diagonal_inversa[15]; RANGO: 2..16 En C, el primer elemento de cada vector tiene ndice 0, esto es fcil 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 posicin 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 acta sobre todas las piezas situadas en la misma columna, fila o diagonal del tablero se deduce que cada columna puede contener una y slo una reina, y que la eleccin de la situacin de la reina i-sima puede restringirse a los cuadros de la columna i. Por tanto, el parmetro i se convierte en el ndice de columna, y por ello el proceso de seleccin de posiciones queda limitado a los ocho posibles valores del ndice de fila j. A partir de estos datos, la lnea poner reina del pseudocdigo es: c (i) = j; f (j) = di (i + j) = dn (i - j) = FALSE; y la lnea quitar reina del pseudocdigo: f (j) = di (i + j) = dn (i - j) = TRUE; y la condicin segura del pseudocdigo:

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: */ 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");

} else printf ("\n\nNO HAY SOLUCION.\n"); } void ensayar (int i) { int j = 0; do { j++; 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; } else acertado = TRUE; } } while (! acertado && j != 8); } Se puede decir que la recursividad es una tcnica de programacin bastante til y muy interesante de estudiar. A travs de los ejemplos que el individuo pueda revisar, aprender con ms rapidez y sencillez lo que es programar recursivamente e incluir esta tcnica cuando se le presente un problema como los que fueron mencionados anteriormente. La asignacin de memoria, sea esttica o dinmica, en realidad se tendr que aplicar en cualquier programa al momento de su codificacin; 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 libre Saltar a: navegacin, bsqueda Se ha sugerido que este artculo o seccin sea fusionado con

Un algoritmo recursivo es un algoritmo que expresa la solucin de un problema en trminos de una llamada a s mismo. La llamada a s mismo se conoce como llamada recursiva o recurrente. FUNCIN Factorial(n) VAR resultado: Entero SI (n<2) ENTONCES resultado = 1; SINO resultado = n * Factorial(n-1); FSI RETORNA resultado; FFUNCIN Generalmente, si la primera llamada al subprograma se plantea sobre un problema de tamao u orden N, cada nueva ejecucin recurrente del mismo se plantear sobre problemas, de igual naturaleza que el original, pero de un tamao menor que N. De esta forma, al ir reduciendo progresivamente la complejidad del problema que resolver, llegar un momento en que su resolucin sea ms o menos trivial (o, al menos, suficientemente manejable como para resolverlo de forma no recursiva). En esa situacin diremos que estamos ante un caso base de la recursividad. Las claves para construir un subprograma recurrente son:

Cada llamada recurrente se debera definir sobre un problema de menor complejidad (algo ms fcil de resolver). Ha de existir al menos un caso base para evitar que la recurrencia sea infinita.

Es frecuente que los algoritmos recurrentes sean ms ineficientes en tiempo que los iterativos aunque suelen ser mucho ms breves en espacio. Recursividad indirecta Cuando en una subrutina hay llamadas a ella misma se habla de recursividad directa, en contraposicin, cuando se tienen varias subrutinas y estas se llaman unas a otras formando ciclos se dice que la recursin es indirecta. Subrutina_A Subrutina_B Subrutina_A

Subrutina_A Subrutina_A

Subrutina_B

Subrutina_C

Subrutina_D

Aplicaciones
Computacin. Informtica. Sistemas Expertos. Inteligencia artificial. Prueba de Turing. Bsqueda heurstica. Algoritmos de bsqueda. Visin artificial. Redes neuronales. Robtica. Hardware. Comunicacin. Lenguajes. Aplicaciones Comerciales Pero tambin la AI tiene numerosas aplicaciones comerciales en el mundo de hoy. Vase: Configuracin: seleccin de distribucin de los componentes de un sistema de computacin. Diagnosis: hardware informtico, redes de ordenadores, equipos mecnicos, problemas mdicos, averas telefnicas, instrumentacin electrnica, circuitos electrnicos, averas automovilsticas.

Interpretacin y anlisis: datos geolgicos para prospeccin petrolfera, compuestos qumicos, anlisis de seales, problemas matemticos complejos, evaluacin de amenazas militares, anlisis de circuitos electrnicos, datos biolgicos (coronarios, cerebrales y respiratorios), informacin de radar, sonar e infrarrojos. Monitorizacin: equipos, monitorizacin de procesos, fabricacin y gestin de procesos cientficos, amenazas militares, funciones vitales de pacientes hospitalizados, datos financieros en tiras de papel perforado por teleimpresora, informes industriales y gubernamentales. Planificacin: gestin de activo y pasivo, gestin de cartera, anlisis de crditos y prstamos, contratos, programacin de trabajos de taller, gestin de proyectos, planificacin de experimentos, produccin de tarjetas de circuito impreso. Interfaces inteligentes: hardware (fiscal) de instrumentacin, programas de computadora, bases de datos mltiples, paneles de control. Sistemas de lenguaje natural: interfaces con bases de datos en lenguaje natural, gestin de impuestos (ayudas para contabilidad), consultora en temas legales, planificacin de fincas, consultora de sistemas bancarios.

Sistemas de diseo: integracin de microcircuitos en muy alta escala, sntesis de circuitos electrnicos, plantas qumicas, edificios, puentes y presas, sistemas de transporte. Sistemas de visin computarizada: seleccin componentes, ensamblado, control de calidad Desarrollo de software: programacin automtica. Desafos tcnicos del futuro Los tres desafos ms 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 ms 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 solucin de problemas del futuro. Sin embargo, el intelecto humano parece ser irremplazable en relacin con la solucin de problemas de sentido comn. Se sugiere entonces, dado lo complicado de la mente humana, que hombre y mquina interacten juntos ya que necesitan uno del otro para solucionar eficazmente los problemas. 6. APLICACIONES DE LA INTELIGENCIA ARTIFICIAL Y LAS TCNICAS QUE USAN Dentro del enfoque de la ingeniera de la Inteligencia Artificial, se clasifican las tcnicas que pueden ser usadas como herramientas para solucionar problemas en las siguientes categoras: 1. Tcnicas bsicas, as llamadas por encontrarse a la base de diversas aplicaciones de IA. Entre otras se encuentran Bsqueda Heurstica de Soluciones, Representacin del Conocimiento, Deduccin Automtica, Programacin Simblica (LISP) y Redes Neuronales. Estas tcnicas son las bases de las aplicaciones. En su mayora, no necesita conocerla el usuario final, sino los profesionales que se dedican a su aplicacin y la generacin de aplicaciones comerciales. 2. Tecnologas, o combinaciones de varias tcnicas bsicas, orientadas a resolver familias de problemas. de piezas y

Las tecnologas son ms especializadas que las tcnicas bsicas y estn ms cerca de las aplicaciones finales. Se pueden mencionar a la Robtica y Visin, Lenguaje Natural, Sistemas Expertos 3. Clases o tipos de aplicaciones: Diagnstico, Prediccin (sistemas de autocontrol de reactores atmicos), Secuenciamiento de operaciones ("Scheduling"), Diseo, Interpretacin de datos. Todas ellas son familias de problemas tipo. Por ejemplo, el diagnstico se refiere a encontrar las causas de fallas, ya sea que se trate de fallas en una lnea de produccin o de enfermedades en una persona. 4. Campos de aplicacin: Ingeniera, Medicina, Sistemas de Manufactura, Administracin, 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. APLICACIN DE LA INTELIGENCIA ARTIFICIAL EN LOS SISTEMAS PRODUCTIVOS La incorporacin de agentes de decisin inteligente, redes neuronales, sistemas expertos, algoritmos genticos y autmatas programables para optimizacin de sistemas de produccin es una tendencia activa en el ambiente industrial de pases con alto desarrollo tecnolgico y con una gran inversin en investigacin y desarrollo. Dichos componentes de la Inteligencia Artificial tienen como funcin principal controlar de manera independiente, y en coordinacin con otros agentes, componentes industriales tales como celdas de manufactura o ensamblaje, y operaciones de mantenimiento, entre otras. Existe una tendencia creciente a la implementacin de sistemas de manufactura/ensamblaje ms autnomos 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 pases subdesarrollados como el nuestro no alcance niveles competitivos a nivel mundial. Al disear un sistema de produccin integrado por computadora se debe dar importancia a la supervisin, planificacin, secuenciacin cooperacin y ejecucin de las tareas de operacin en centros de trabajo, agregado al control de los niveles de inventario y caractersticas de calidad y confiabilidad del sistema. Los factores mencionados determinan la estructura del sistema y su coordinacin representa una de las funciones ms importantes en el manejo y control de la produccin. Muy frecuentemente, la razn para construir un modelo de simulacin es para encontrar respuestas a interrogantes tales como Cules son los parmetros ptimos para maximizar o minimizar cierta funcin objetivo? En los ltimos aos se han producido grandes avances en el campo de la optimizacin de sistemas de produccin.

Sin embargo, el progreso en el desarrollo de herramientas de anlisis para resultados de modelos de simulacin ha sido muy lento. Existe una gran cantidad de tcnicas tradicionales de optimizacin que slo individuos con gran conocimiento estadstico y de conceptos de simulacin han logrado aportes significativos en el rea. Debido al auge de los algoritmos de bsqueda meta-heursticos, se ha abierto un nuevo campo en el rea de optimizacin con simulacin. Nuevos paquetes de software, tales como OptQuest (Optimal Technologies), SIMRUNNER (Promodel Corporation) y Evolver (Palisade Software), han salido al mercado brindando soluciones amigables de optimizacin de sistemas que no requieren control interno sobre el modelo construido, sino sobre los resultados que dicho modelo arroja bajo diferentes condiciones. Adems, nuevas tcnicas de inteligencia artificial aplicadas a problemas de optimizacin estocstica, han demostrado su eficiencia y capacidad de cmputo y aproximacin. El Aprendizaje Reforzado (Reinforcement Learning) es un conjunto de tcnicas diseadas para dar solucin a problemas cuya base son los procesos de decisin markovianos. Los procesos markovianos son procesos estocsticos de decisin que se basan en el concepto de que la accin a tomar en un estado determinado, en un instante determinado, depende slo del estado en que se encuentre el sistema al momento de tomar la decisin. Una de las reas que puede tener mayor incidencia directa en los procesos productivos de la industria nivel mundial, es el diseo de sistemas de soporte para la toma de decisiones basados en la optimizacin de los parmetros de operacin del sistema. P Para tal efecto, el uso de tcnicas inteligentes paramtricas y no paramtricas para el anlisis de datos es de gran inters. Sin embargo, a juicio de los autores en la mayora de las arquitecturas propuestas hasta el momento para manufactura integrada por computadora, carecen de un factor de integracin fundamental. La comunicacin entre los diversos niveles jerrquicos de una planta de produccin es muy poca, ya que cada departamento se limita a realizar su funcin sin buscar una integracin 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 Operacin automtica de control de calidad usando un sistema de visin 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 varan dependiendo de los parmetros que tengan relevancia en el mismo. Cuando el parmetro relevante es la geometra o forma del objeto fabricado se suele dejar a la vista del operario que lleve a cabo tal funcin tanto de inspeccin como de verificacin para el control de calidad, sin embargo pueden existir errores en la geometra de un objeto que escapen de la vista de un operario y que luego impidan el buen funcionamiento de dicho objeto. En un caso como ste, surge como una buena alternativa el utilizar un sistema de visin artificial capaz de detectar aquellos errores que un operario pudiera pasar por alto. El sistema de visin artificial Robot Visin PRO, es capaz de ejecutar de manera totalmente automtica las labores de identificacin de objetos y de control de calidad de los mismos. El sistema Robot Visin PRO es un paquete de software de visin que permite la adquisicin de imgenes, preprocesamiento y segmentacin. Adems realiza procesamiento de datos de alto nivel que brinda filtrado de imgenes, elaboracin de clusters y patrones, e identificacin de objetos. Este sistema cuenta con una videocmara y un monitor encargado de identificar cada una de las piezas salientes del proceso y hacer una comparacin con piezas de 100% calidad para luego determinar si el empaque puede salir al mercado o debe desecharse. A continuacin se presentan algunas imgenes suministradas por el sistema Robot Visin PRO Para la ejecucin de la operacin de control de calidad. Fueron dispuestos los empaques de tal forma que las geometras 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 grfico seleccione la opcin "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 grfico seleccione la opcin "Descargar" del men superior <> Empaque rechazado por mala calidad Para ver el grfico seleccione la opcin "Descargar" del men superior Figura 5. Empaque rechazado por mala calidad El sistema de visin por computador Robot Visin PRO despus de ser evaluado en la empresa result eficiente para la deteccin de defectos

geomtricos en los empaques de compresores centrfugos, ya que la flexibilidad del software permiti ajustar las condiciones del proceso al sistema de calidad requerido para la apropiada medicin de los empaques. Este sistema es lo bastante didctico como para desarrollar expresiones que permitan realizar de manera totalmente automtica mediciones del objeto, labores de reconocimiento y de control de calidad. Los autores opinan que es muy adecuado el uso de esta tecnologa en empresas donde el acabado superficial de una pieza sea muy exigente estrechas tolerancias como por ejemplo repuestos de carros, instrumentacin 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 travs de despacho y control inteligente que permita mejorar la calidad del servicio y reduzca los costos de operacin. 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 travs de un computador y una lnea telefnica desde cualquier lugar del mundo y a travs de Internet. 2.Proyectos en va de desarrollo por la lnea de investigacin y desarrollo de inteligencia artificial (grupo de investigacin de la Universidad de Manizales) Este estudio est centrado en la identificacin global de ambientes ejecutada por un robot mvil con base en el entrenamiento de una red neuronal que recibe la informacin captada del medio ambiente por el sistema sensorial del robot (ultrasonido). Se considera que el robot, nica tares maximizar el presenta. De esta forma eficientemente mientras obstculos. a travs de la red neuronal, tiene como conocimiento del ambiente que se le este modela y explora el ambiente ejecuta algoritmos de evasin de

El resultado de este estudio es de gran importancia en el campo de la robtica mvil debido a que: el robot adquiere una mayor autonoma del movimiento, se optimiza el uso del ultrasonido como detector de obstculos y es una herramienta importante para el desarrollo de planificadores de trayectoria y controladores inteligentes. Usando una arquitectura: 2 - 2 -1 Nih: Nmero de neuronas de entrada(2). Nhid: Nmero de neuronas de la capa intermedia(1). Nout: Nmero de neuronas de salida(2).

Se va a mostrar a groso modo uno de los ejemplos con los cules fue entrenada la red (para mayor detalle consultar investigacin de Rivera & Gauthier [1995] Universidad de los Andes). Los parmetros usados en el entrenamiento fueron constante de aprendizaje de 0.2 y constante de momento de 0.9 Para ver el grfico seleccione la opcin "Descargar" del men superior Ambiente de entrenamiento de tres obstculos 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 ningn obstculo. En la red neuronal a medida que se aumenten las capaz internas tendr ms capacidad y velocidad de aprender diversos ambientes. A intervencin de los autores, determinan que es muy importante el uso de la robtica mvil 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 podra entrenar un robot y a medida que se perfeccione su entrenamiento prepararlo posteriormente como transportador de carga. 3. Reconocimiento de ambientes en robtica mvil por medio de redes neuronales 4.Algoritmos genticos aplicados al problema cuadrtico de asignacin de facilidades QAP (Departamento de Investigacin Operativa, Escuela de Ingeniera Industrial, Universidad de Carabobo, Valencia, Venezuela. Ninoska Maneiro. Algoritmo Gentico Aplicado a Problemas de Localizacin de Facilidades. Ao 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 asignacin de facilidades a sitios, a fin de minimizar una funcin que expresa costos o distancias. La localizacin y distribucin de facilidades es uno de los tpicos ms importantes en la formacin de profesionales en el rea de Ingeniera Industrial y de todos aquellos profesionales que se encargan de la planificacin, organizacin y crecimiento sistemtico de las ciudades. En la vida cotidiana y profesional de todo individuo, se presentan una gran variedad de problemas de localizacin de facilidades. Los problemas de localizacin y distribucin de facilidades son estratgicos para el xito de cualquier operacin de manufactura.

La principal razn es que los costos de manejo al de materiales comprenden entre el 30 y el 75% de los costos totales de manufactura. Una buena solucin problema de asignacin de facilidades contribuira a la eficiencia total de las operaciones, una pobre distribucin puede conducir a la acumulacin 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 lnea generalizado, que es una lnea de flujo en la cual las operaciones fluyen hacia adelante y no se procesan necesariamente en todas las mquinas de la lnea. Un trabajo en tal clase de lnea puede comenzar a procesarse y completar su proceso en cualquier mquina, movindose 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 mquina colocada delante de su localizacin actual, el trabajo tiene que viajar en sentido contrario (upstream) a fin de completar la operacin requerida. Este <>"viaje en reversa" de las operaciones, es llamado backtracking, y se desva de una lnea de flujo ideal para un trabajo especfico, resultando en una estructura de trabajo menos eficiente, como se muestra en la siguiente figura. Al parecer de los autores, este problema de asignacin cuadrtica debera tratarse en la clase de taller de produccin por su relevancia al analizar secuencias N / M.

Una lnea de flujo generalizada Fuente: Ninoska Maneiro 2001. 9. CONCLUSIONES En la universidad Nacional sede Manizales en el programa de ingeniera industrial se debera trabajar ms en las ciencias informticas, con el fin, de profundizar en reas de la inteligencia artificial aplicadas a la ingeniera industrial. Con el desarrollo de este trabajo se han obtenido resultados satisfactorios a nivel de investigacin terica, ya que con la documentacin obtenida se conocieron avances en las ciencias

informticas que en algunos casos eran desconocidos para los autores. Los grandes avances de I.A aplicada a sistemas de produccin han hecho que da a da la industria en su constante bsqueda 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.

También podría gustarte