Sei sulla pagina 1di 91

UNIVERSIDAD NACIONAL DE GUINEAECUATORIAL

FACULTAD DE CIENCIAS ECONOMICAS, GESTION Y ADMON.


2016
DEPARTAMENTO DE INFORMATICA DE GESTION EMPRESARIAL

Asignatura
TECNOLOGIA DE PROGRAMACION
2º Curso

Profesor:
Hakim Pergentino ESIMI MBOMIO ANGUÉ

ING. DE SISTEMAS

Tel: 551 296 103 - 222 749 445


Email: perdupri@gmail.com
PeRgeN
Twitter: @PergentinoDupri
---------------------------------------
MALABO R.G.E.
04/10/2016
TEMARIO DE LA ASIGNATURA

PARTE I: ESTRUCTURAS DE DATOS Y ALGORITMOS


INTRODUCCION................................................................................................................................................. 5
TEMA 1: CONCEPTOS BÁSICOS Y ANÁLISIS ASINTÓTICO DE ALGORITMOS.....................5
1.1 ALGORITMO............................................................................................................................................. .5
1.2 ESTRUCTURA Y FUNCIONAMIENTO DE UN COMPUTADOR.PROGRAMAS………….. 7
1.3 ETAPAS DE DESARROLLO DEL SOFTWARE………………......................................................9
1.4 CARACTERÍSTICAS FUNDAMENTALES DE UN ALGORITMO.............................................9
1.4.1 ALGORITMO COMO MÉTODO DE SOLUCIÓN DE PROBLEMAS …………….…............10
1.4.2 FASES DE DESARROLLO DE UN ALGORITMO.......................................................................10
1.5 ANALISIS DE ALGORITMOS............................................................................................................12
1.5.1 ESTRUCTURA DE DATOS I..........................................................................................................13
1.5.2 ESTRUCTURA DE DATOS II........................................................................................................14
1.6 TIEMPO DE EJECUCIÓN...................................................................................................................14
1.7 ANÁLISIS ASINTÓTICO...................................................................................................................15
1.8 ORDENES DE COMPLEJIDAD........................................................................................................16
1.9 IMPACTO PRÁCTICO.........................................................................................................................16
1.10 PROPIEDADES DE LOS CONJUNTOS O(f)..............................................................................18
1.11 REGLAS PRÁCTICAS......................................................................................................................18
1.11.1 SENTENCIAS SENCILLAS........................................................................................................18
1.11.2 SECUENCIA(;)............................................................................................................................... 18
1.11.3 DECISIÓN (if)................................................................................................................................18
1.11.4 BUCLES............................................................................................................................................18
1.11.5 LLAMADAS A PROCEDIMIENTOS.........................................................................................19
1.11.6 Ejemplo: EVALUACIÓN DE UN POLINOMIO.....................................................................19
1.11.7 MEDIDAS DE LABORATORIO.................................................................................................21
1.11.8 PROBLEMAS P, NP y NP-completos....................................................................................22
1.11.9 CONCLUSIONES............................................................................................................................23
TEMA 2: Vectores y algoritmos de ordenación............................................................................24
2.1 VECTOR...................................................................................................................................................24
2.2 CREAR UN VECTOR..........................................................................................................................27
2.3 VECTORES DINÁMICOS Y ESTÁTICOS...................................................................................28
2.3.1 DECLARACIÓN EN C++ DE UN VECTOR DE STL:.............................................................29
2.3.2 VECTORES MULTIDIMENSIONALES.....................................................................................30
2.4 ALGORITMOS DE ORDENACIÓN................................................................................................30
TEMA 3: LISTA, PILAS Y COLAS........................................................................................................ 36
3.1 LISTAS....................................................................................................................................................36
3.2 PILA.........................................................................................................................................................41
3.3 COLA.......................................................................................................................................................46
TEMA 4: ÁRBOLES Y GRAFOS. REPRESENTACIÓN Y ALGORITMOS DE
RECORRIDO………………………………………………………………………………………………………. 51
4.1.1 REPRESENTACIÓN........................................................................................................................51
4.1.2 RECORRIDO DE GRAFOS. ..........................................................................................................53
4.1.2.1 EL CICLO EULERIANO...............................................................................................................53
4.1.2.2 EL CICLO HAMILTONIANO.....................................................................................................53
4.2 ÁRBOLES....................................................................................................................................... ........54
4.2.1 REPRESENTACIONES...................................................................................................................56
4.2.2 ALTURA...............................................................................................................................................57
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 2 de 91
4.2.3 PROFUNDIDAD................................................................................................................................58
4.2.4 RECORRIDO DE ÁRBOLES..........................................................................................................58
4.2.5 VARIANTES DE ÁRBOLES BINARIOS....................................................................................62
TEMA 5: OTRAS ESTRUCTURAS DE DATOS: MONTÍCULO (HEAP) Y CONJUNTOS… .65
5.1 MONTÍCULO (HEAP).........................................................................................................................65
5.1.1 MÉTODO DE ORDENAMIENTO HEAPSORT........................................................................66
5.1.2 CONSTRUCCIÓN DE UN MONTÍCULO...................................................................................67

PARTE II: DISEÑO DE ALGORITMOS

TEMA 6: RECURSIVIDAD Y DISEÑO ITERATIVO...........................................................................71


6.1 RECURSIVIDAD ..................................................................................................................................71
6.1.1 RECURSIVIDAD EN JAVA.............................................................................................................71
6.1.2 CARACTERÍSTICAS.......................................................................................................................71
6.1.3 LEGIBILIDAD Y FACILIDAD DE COMPRENSIÓN................................................................73
6.2 DISEÑO ITERATIVO...........................................................................................................................74
6.2.1 RECURSIÓN.......................................................................................................................................74
6.2.2 ITERACIÓN.......................................................................................................................................74
6.2.2.1 DIFERENCIA PRINCIPAL........................................................................................................74
6.2.2.2 VENTAJAS Y DESVENTAJAS:.................................................................................................75
6.2.2.3 SEMEJANZAS Y DIFERENCIAS:............................................................................................75
TEMA 7: ALGORITMOS VORACES Y ALGORITMOS DIVIDE Y VENCERÁS........................ 76
7.1 ALGORITMOS VORACES. INTRODUCCIÓN. ESQUEMA VORAZ…………………....……..76
7.2 ALGORITMOS DIVIDE Y VENCERÁS............…………….…….…………….…….…….….............78
TEMA 8: ALGORITMOS DE EXPLORACIÓN DE GRAFOS............................................................85
8.1 GRAFOS..................................................................................................................................................85
8.2 EXPLORACIÓN DE GRAFOS...........................................................................................................85
8.3 TIPOS DE GRAFOS.............................................................................................................................85
8.3.1 GRAFOS BI-DIRECCIONALES....................................................................................................85
8.3.2 Grafos unidireccional...................................................................................................................86
8.3.3 Grafos de Peso.................................................................................................................................86
PARTE III: INTELIGENCIA ARTIFICIAL
TEMA 9: TÉCNICAS DE BÚSQUEDA HEURÍSTICA EN GRAFOS……………….……………… 88
9.1 EJEMPLOS DE FUNCIONES HEURÍSTICAS..................................………………….…………..... 88
9.2 MÉTODOS DE ESCALADA...............................................................……………………..………......89
9.2.1 MÉTODOS DE ESCALADA IRREVOCABLES......................…………………………..…........ 89
9.2.1.1 LA ESCALADA SIMPLE............................ ................….......................…………………..……... 90
9.2.1.2 LA ESCALADA POR LA MÁXIMA PENDIENTE…......................……………………...…... 90
9.2.2 ALGORITMO DE ESCALADA SIMPLE..........……………...…………………………….............89
TEMA 10: APRENDIZAJE AUTOMÁTICO………………………..…………………………………....… 90
10.1INTRODUCCIÓN AAPRENDIZAJE AUTOMÁTICO ………...………………………………… 90
10.2 TIPOS DE APRENDIZAJE AUTOMÁTICO……………………..………………………………… 90
10.2.1 APRENDIZAJE SUPERVISADO ………………………………..……………………………….…91
10.2.2 APRENDIZAJE NO SUPERVISADO ……………………………..………………………….……92
10.2.3 APRENDIZAJE POR REFUERZO …………………………………..……………….……………92

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 3 de 91
PARTE I:

ESTRUCTURAS DE DATOS Y ALGORITMOS

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 4 de 91
INTRODUCCIÓN.
Empezaremos con unas definiciones un poco técnicas, pero que son
imprescindibles para el aprendizaje.

TECNOLOGÍA:
Dentro del campo que nos interesa a nosotros, la tecnología tiene el
objetivo de eliminar las tareas repetitivas, facilitando el trabajo y
haciéndolo más eficiente así como aumentando la productividad y los
beneficios de la empresa.
la tecnologia aunque tenga varias definiciones, la definimos como un
conjunto de los conocimientos, instrumentos y metodos tecnicos
empleados en un sector profesional.

Programación:
La programación es una de las etapas más importantes del ciclo de vida
de un proyecto (explicaremos más adelante lo que es este término), y
requiere un método de trabajo. La programación es el resultado de dicho
trabajo.
La programación es el instrumento que permite la ejecución de las
tareas automatizadas de un sistema informático.
Las herramientas que utilizaremos para programar son los lenguajes de
programación, a través de las cuales codificaremos los programas.

Programa:
Conjunto de instrucciones entendibles por el ordenador que permiten
realizar un trabajo o resolver un problema.
Un programa debe ser finito, es decir, tiene que tener un inicio y un
fin. Tiene que estar bien confeccionado para que, al introducir un
dato, salga una solución y si se volviese a introducir el mismo dato,
saliese de nuevo la misma solución.

Metodología de la programación:
Se entiende como metodología de la programación al conjunto de normas,
métodos y anotaciones que nos indican la forma de programar.
Cada lenguaje de programación sigue una metodología distinta.

Lenguaje de programación:
Es un conjunto de reglas semánticas así como sintácticas que los
programadores usan para la codificación de instrucciones de un programa
o algoritmo de programación.
Existen varios lenguajes de programación.

Entorno de programación o entorno de desarrollo:


Es el conjunto de herramientas utilizadas para la elaboración de un
programa.

Recursos:
Conjunto de componentes hardware que utilizaremos para la elaboración
de un programa (cpu, disco duro, etc.).

Una vez conocidos los conceptos básicos necesarios para el aprendizaje


de la programación podemos empezar a ver los diferentes tipos de
lenguajes de programación.
El desarrollo de software es una de las tareas más importantes dentro
del ámbito de la informática.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 5 de 91
TEMA 1:CONCEPTOS BÁSICOS Y ANÁLISIS ASINTÓTICO DE ALGORITMOS.
1.1 ALGORITMO. ¿Qué es?
Definición 1.1.1 (Algoritmo) Descripción no ambigua y precisa de una
secuencia de accionesque permite resolver un problema bien definido en
tiempo finito.
Por lo tanto, básicamente un algoritmo es la descripción de cómo
resolver el problema; pero,además, en la definición aparecen una serie
de calificativos que nos indican qué características se están
imponiendo a esta descripción:
No ambigua: Cada acción debe tener una única interpretación.
Precisa: Cada acción debe estar definida rigurosamente.
Como consecuencia de estas dos premisas se tiene que el lenguaje
natural no es un lenguajeapropiado para definir algoritmos, ya que
puede dar lugar a interpretaciones según el oyente.
Secuencia: No se refiere sólo a una sucesión más o menos larga de
instrucciones: el orden que ocupa una instrucción en esa secuencia es
significativo.
Problema bien definido: Debe conocerse de qué datos se va disponer y
cuáles son los resultados esperados.
Además, se ha de tener en cuenta que un buen algoritmo debe ser un
método general para resolver todos los casos posibles del mismo
problema.
Tiempo finito: Los algoritmos deben finalizar.
Relacionados con el concepto de algoritmo, se tienen los siguientes:

Definición 1.1.2 (Entorno) Conjunto de objetos necesarios para llevar a


término una tarea.

Como en toda disciplina, en programación se debe conocer qué objetos se


pueden manejar ycómo se deben manejar. Este será el objetivo principal
del próximo tema.
Asociado al concepto de entorno, aparece el de estado del entorno, es
decir, la descripción del estado en que se encuentran los objetos del
entorno en un momento dado.
Un algoritmo debe actuar para que el entorno cambie progresivamente de
estado. Así se iránobteniendo los resultados a partir de los datos.

Definición 1.1.3 (Acción) Un suceso finito, con un efecto definido y


previsto.

De nuevo es preciso puntualizar las características descritas.


Finito: La acción debe finalizar.
Efecto definido: También denominado determinismo: las acciones tienen
una interpretación única.
Efecto previsto: Se deben conocer las consecuencias de las acciones.
Definición 1.1.4 (Proceso) El resultado de ejecutar una o varias
acciones.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 6 de 91
Definición 1.1.5 (Procesador) La entidad que puede comprender y
ejecutar de forma eficaz unalgoritmo.

Se han hecho ya muchas definiciones y aún no se ha dicho nada sobre los


programas. Paradistinguir este concepto, se cree conveniente describir
antes cómo funciona un ordenador.

1.2 ESTRUCTURA Y FUNCIONAMIENTO DE UN COMPUTADOR (PROGRAMAS)


En un computador se distinguen tres partes principales (fig. 1.1):
Las Unidades de Entrada y/o Salida, que permiten la comunicación con el
computador (introducir datos e instrucciones, visualizar resultados,
etc.)
La Unidad Central de Proceso, que es la que dirige el funcionamiento de
la máquina.
Memoria Central, que es donde se almacenan los datos y los resultados,
así como las instrucciones. Se organiza internamente en posiciones de
memoria, y en cada una de estas posiciones se almacena una cantidad
determinada de información (una palabra de memoria).
¿Cómo funciona? La Unidad Central de Proceso tiene dos componentes
principales: la Unidad de Control y la Unidad Aritmético-Lógica. La
Unidad de Control conoce en todo momento en qué posición de memoria se
encuentra la siguiente instrucción que debe ejecutar el computador y se
encarga de que se ejecute. Primero recoge de la Memoria Central la
información necesaria para ejecutarla (la instrucción y los datos
correspondientes); cuando tiene la instrucción, la interpreta para
saber qué es lo que debe hacerse y la ejecuta. Si es una operación de
tipo aritmético o lógico, se encarga de ordenar a la Unidad Aritmético-
Lógica que la realice y le suministra los datos sobre los que debe
operar. Finalmente, si se produjera algún resultado, la Unidad de
Control lo almacena en la Memoria Central.

Fig. 1.1 Estructura simplificada de un computador.

Este proceso se repite desde la primera instrucción hasta la última,


respetando el orden de la secuencia.
Esta máquina almacena los datos y las instrucciones en memoria en un
determinado formato.Esto obligará a que los algoritmos deban traducirse
a un lenguaje que el computador comprenda, a un lenguaje de
programación.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 7 de 91
Definición 1.2.1 (Programa) Es la codificación de un algoritmo en un
lenguaje de programación.
Una vez que se diseñado el algoritmo, la traducción de este a un
lenguaje de programación para obtener un programa es muy directa. Basta
con conocer el lenguaje de programación a utilizar y disponer de un
procesador del lenguaje que permita generar el código ejecutable por el
computador.
Como ya se ha hecho antes, se puede establecer una analogía entre el
esfuerzo intelectual de escribir una novela y el de escribir un
algoritmo; la generación del programa a partir del algoritmo sería
equivalente al esfuerzo de traducir una novela a otro idioma.

Fig. 1.2 La Programación como Disciplina de Ingeniería.

Hay dos tipos de procesadores de lenguaje, Intérpretes y Compiladores.


En ambos casos se trata de programas que pueden traducir texto escrito
en un lenguaje de programación concreto a instrucciones del lenguaje
máquina, directamente ejecutables por el computador. Y es entonces
cuando el computador comienza a trabajar tal y como se ha descrito
anteriormente. Y, de nuevo, el proceso es automático: el único paso
creativo en este proceso es el diseño del algoritmo; tanto la codificación a
un lenguaje de programación como la traducción posterior a lenguaje
máquina como la posterior ejecución no suponen esfuerzo creativo
alguno. Por lo tanto, si se producen malos resultados al ejecutar un
programa nunca es debido ni al computador ni al procesador del
lenguaje.Los malos resultados son consecuencia de malos algoritmos.
En esta asignatura, no sólo se aprenderá a diseñar algoritmos, también
se implementarán en diferentes lenguajes de programación para
ejecutarlos sobre un computador.
Entre otros, hay dos tipos de paradigmas de programación: el imperativo
y el declarativo. Este último, a su vez, se divide en el paradigma
lógicoy en el funcional.
En esta asignatura se utilizará la programación imperativa, que
consiste en diseñar los algoritmos (por lo tanto, los programas)
mediante una secuencia de instrucciones que definen cómo resolver un
determinado problema (por lo tanto, es como si se diseñara una
secuencia de “órdenes” que el computador debe seguir para obtener los
resultados a partir de los datos). Por contra, en programación
declarativa, los algoritmos son secuencias de instrucciones que definen
qué problema hay que resolver, no cómo.
Además de seguir diferentes estilos de programación, es posible
utilizar diferentes metodologías de programación. En esta asignatura se
utilizará la programación estructurada, que se introducirá enlos temas
2 y 3, para asegurar la legibilidad de los diseños realizados y una de
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 8 de 91
las metodologías dediseño más simples, la denominada TopDown o de
Refinamiento Progresivo, que se introducirá enel tema 4.

1.3 ETAPAS DE DESARROLLO DEL SOFTWARE.


El proceso de desarrollo de programas, dentro del paradigma imperativo,
se divide en variasetapas, que se enumeran a continuación:
1. Definición del Problema y Análisis de Requisitos.
2. Diseño del Algoritmo.
3. Implementación.
4. Pruebas.
5. Operación, Mejoras y Mantenimiento.

La última fase se realiza realmente durante la vida operativa del


programa diseñado; si fuera necesario hacer mejoras o cambios, se
habría de retroceder a etapas previas del desarrollo.
Lo deseable es que estos pasos se ejecuten en secuencia, es decir, que
no comience una etapa hasta que no haya finalizado la anterior. Esta
sería la consecuencia de aplicar una buena metodología de trabajo, ya
que supondría la forma más eficaz y eficiente de trabajar. Y
actualmente se dispone de los conocimientos necesarios para poder
desarrollar así el trabajo.
Sin embargo, todavía es normal que este desarrollo se vea interrumpido
por continuas realimentaciones, mucho antes de llegar a la última
etapa: clientes que cambian la definición del problema, errores
detectados en la etapa de prueba como consecuencia de un mal diseño del
algoritmo... Una buena medida sobre la calidad y la profesionalidad del
programador, o del equipo de programadores, puede ser, precisamente, el
número de realimentaciones que deba realizar en el esquema anterior.
A continuación se describe cada una de estas etapas:
Definición del problema y análisis de requisitos: Un cliente plantea un
problema que necesita una solución informática mediante la construcción
de un sistema informático; en este sistema pueden integrarse varios
equipos, distintos programas y bases de datos. El primer paso consiste
en un análisis del problema y es un paso muy laborioso, bien sea por el
nivel de comprensión del problema, bien por el planteamiento que haya
realizado el cliente. Una vez que se ha definido el sistema (qué
partes, cuáles son, qué programas hay que desarrollar, cómo se deben
integrar, cómo deben colaborar), se debe realizar un análisis minucioso
de los requisitos que supone la definición informática del mismo.
El resultado de esta etapa debe ser un enunciado preciso y claro del
problema.
Esta etapa entra dentro de la disciplina denominada Ingeniería del
software y queda fuera de esta asignatura. Por lo tanto, nosotros
partiremos siempre de un enunciado ya definido, a partir del cual se
desarrollarán las etapas posteriores.

1.4 CARACTERÍSTICAS FUNDAMENTALES DE UN ALGORITMO.


Sabiendo que es un conjunto ordenado y finito de operaciones que
permite hallar la solución de un problema, un algoritmo se caracteriza
por ser:
FINITO: es un algoritmo que siempre debe terminar después de un número
de pasos.
DEFINIDO: si se sigue un algoritmo dos veces se debe obtener el mismo
resultado.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 9 de 91
PRECISO: indica exactamente el orden de realización de las
instrucciones.

1.4.1 ALGORITMO COMO MÉTODO DE SOLUCIÓN DE PROBLEMAS (ejemplo)


1. Buscar herramientas, rueda de repuesto y triángulo de señalización
2. Ubicar el triángulo en el lugar adecuado
3. Ir al lugar de la rueda averiada
4. Sacar las tuercas
5. Colocar el gato

6. Sacar la rueda
7. Colocar la rueda de Repuesto
8. Colocar las tuercas
9. Apretar las tuercas
10. Guardar las herramientas

1.4.2 FASES DE DESARROLLO DE UN ALGORITMO.

 Fase de Análisis: consiste en el estudio detallado del problema con


el fin de obtener una serie de documentos (especificación) en los
cuales quedan totalmente definido el proceso a seguir en la
automatización.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 10 de 91
Estudio detallado Documentos de Especificación

 Diseño: consiste en la realización del algoritmo que resuelve el


problema de acuerdo a la especificación dada en la fase anterior. El
algoritmo se representa mediante pseudocódigo.

Algoritmo PRUEBA
Inicio
Variables
A,B,C: entero
Leer(A,B)
C A+B
Escribir
(C)
Fin

 Documentos deEspecificación: Codificación: consiste en la


traducción del algoritmo a un programa escrito en un lenguaje de
programación

Algoritmo Sumar
Inicio
Variables
A,B,C: entero
Leer(A,B)
C A+B
Escribir (C)
Fin
Programa escritoen un Lenguaje de Programación

Programa fuente Programa Ejecutable

 Prueba: consiste en determinar si el programa funciona correctamente


y realiza las operaciones que esperamos de él.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 11 de 91
Programa Ejecutable Aplicación

 Diseño del algoritmo: En esta etapa se debe realizar un diseño que


permita obtener la solucióndeseada al problema.
Esta es la etapa más importante y costosa del proceso; de su éxito
depende, en buena medida,el coste y el éxito de las etapas posteriores.
Esta etapa es el objetivo básico de esta asignatura.
 Implementación del programa: Para ejecutar el algoritmo hay que
traducirlo a un lenguaje deprogramación. Esta etapa es, pues, una etapa
poco costosa y sencilla ya que se trata de traducirel diseño realizado
en la etapa anterior.

1.5 ANALISIS DE ALGORITMOS.


La resolución práctica de un problema exige por una parte un algoritmo
o método de resolución y por otro un programa o codificación de aquel
en un ordenador real. Ambos componentes tienen su importancia; pero la
del algoritmo es absolutamente esencial, mientras que la codificación
puede muchas veces pasar a nivel de anécdota.
A efectos prácticos o ingenieriles, nos deben preocupar los recursos
físicos necesarios para que un programa se ejecute. Aunque puede haber
muchos parámetros, los más usuales son el tiempo de ejecución y la
cantidad de memoria (espacio). Ocurre con frecuencia que ambos
parámetros están fijados por otras razones y se plantea la pregunta
inversa: ¿cuál es el tamaño del mayor problema que puedo resolver en T
segundos y/o con M bytes de memoria? En lo que sigue nos centraremos
casi siempre en el parámetro tiempo de ejecución, si bien las ideas
desarrolladas son fácilmente aplicables a otro tipo de recursos.
Para cada problema determinaremos una medida N de su tamaño (por número
de datos) e intentaremos hallar respuestas en función de dicha N. El
concepto exacto que mide N depende de la naturaleza del problema. Así,
para un vector se suele utilizar como N su longitud; para una matriz,
el número de elementos que la componen; para un grafo, puede ser el
número de nodos (a veces es más importante considerar el número de
arcos, dependiendo del tipo de problema a resolver); en un fichero se
suele usar el número de registros, etc. Es imposible dar una regla
general, pues cada problema tiene su propia lógica de coste.

Fig. 1.2.3 Algoritmo

1.5.1 ESTRUCTURA DE DATOS I


DATO: Un tipo de datos es una colección de valores.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 12 de 91
Un tipo de datos abstracto (TDA) es un tipo de datos definido de forma
única mediante un conjunto dado de operaciones definidas sobre este
tipo.Una estructura de datos es una implementación física de un tipo de
datos abstracto. El diseño de una estructura de datos se realiza en
tres fases:
 Análisis de datos y operaciones
 Elección de TDA (tipo de datos abstractos)
 Elección de una implementación

Tipo de datos básicos:


 Entero
 Real
 Booleano
 Carácter

Tipo de datos escalares:


El conjunto de valores es ordenado y cada valor es atómico.
 Carácter
 Entero
 Real
 Booleano

Tipo de datos ordinales:


Cada valor tiene un único predecesor (excepto el primero) y un único
sucesor (excepto el ultimo). Las tres funciones asociadas son:
 Posición (Ord)
 Predecesor (Pred)
 Sucesor (Suc)
De un elemento. Ejemplo: carácter, real, entero, booleano.

Tipos de datos compuestos:


Son visibles en componentes que pueden ser accedidas individualmente.
Operaciones asociadas son las de almacenar y recuperar componentes
individuales. Los TDA compuestos básicos son:
 TDA conjunto (colección de tratados con las operaciones de unión,
inserción y diferencia de conjuntos)
 TDA arreglo (colección homogénea de longitud fija, cada elemento
se accede mediante índice de tipo ordinal que indica la posición
de componente dentro de la colección)

NOTA: la abstracción es la acción de aislar. De considerar en la mente, una parte como


separado de un dato. La abstracción caracteriza la realidad, ejemplo en la siguiente
línea aparecen muchos números aparentemente inconexos. 1,2,3,2.5, -33, -12.3 un
conjunto de numero que se puede clasificar en función de criterios donde encontramos
positivos y negativos.

1.5.2 ESTRUCTURA DE DATOS II


TDA registro: Tipo de datos homogéneo compuesto por un número fijo de
componentes denominados de campos a las que se accede mediante un
selector de campo.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 13 de 91
TDA conjunto:No es estructurado ya que no está organizado el modo de
acceso a sus componentes, mientras que el arreglo y el registro si lo
son.
Tipo TDA lista (o secuencia):colección homogénea de datos, ordenados
según su posición tal que cada elemento tiene un predecesor (excepto el
primero) y un único sucesor (excepto el ultimo). Las operaciones
asociadas son:
 Insertar(inserta un elemento x en la posición p de la lista)
 Localizar(localiza la posición p en la que se encuentra el dato x)
 Recuperar (encuentra l elemento x que está en la posición p)
 Suprimir (elimina de la lista el elemento de la posición p)
 suprimirData (elimina de la lista cualquier aparición de elemento x)
 anula (ocasiona que la lista se vacíe la lista)
 primero (fin) proporciona el primer elemento de la lista
 imprimir, imprime todos los elementos de la lista en su orden.

Ejemplo:Supongamos las siguientes lista de números enteros


4, 0, -9, 7, 6, -4, 5, 4, -3, 0, 4 (posición de 1 a 11)
La operación insertar (-5, 4) inserta -5 en la posición 4, por tanto,
la lista resultante es: 4, 0, -9, 7, -5, 6, -4, 5, 4, -3, 0, 4.

1.6 TIEMPO DE EJECUCIÓN


Una medida que suele ser útil conocer es el tiempo de ejecución de un
programa en función de N, lo que denominaremos T(N). Esta función se
puede medir físicamente (ejecutando el programa, reloj en mano), o
calcularse sobre el código contando instrucciones a ejecutar y
multiplicando por el tiempo requerido por cada instrucción. Así, un
trozo sencillo de programa como
S1; for (int i= 0; i < N; i++)
S2;requiereT(N)= t1 + t2*N
(donde N es un parámetro, siendo t1 el tiempo que lleve ejecutar la
serie "S1" de sentencias, y t2 el que lleve la serie "S2").
Prácticamente todos los programas reales incluyen alguna sentencia
condicional, haciendo que las sentencias efectivamente ejecutadas
dependan de los datos concretos que se le presenten. Esto hace que más
que un valor T(N) debamos hablar de un rango de valores
Tmin(N) <= T(N) <= Tmax(N)
los extremos son habitualmente conocidos como "caso peor" y "caso
mejor". Entre ambos se hallara algún "caso promedio" o más frecuente.
Cualquier fórmula T(N) incluye referencias al parámetro N y a una serie
de constantes "Ti" que dependen de factores externos al algoritmo como
pueden ser la calidad del código generado por el compilador y la
velocidad de ejecución de instrucciones del ordenador que lo ejecuta.
Dado que es fácil cambiar de compilador y que la potencia de los
ordenadores crece a un ritmo vertiginoso (en la actualidad, se duplica
anualmente), intentaremos analizar los algoritmos con algún nivel de
independencia de estos factores; es decir, buscaremos estimaciones
generales ampliamente válidas.

1.7 ANÁLISIS ASINTÓTICO


Este análisis consiste en analizar la potencia de los algoritmos
independientemente de la potencia de la máquina que los ejecute e
incluso de la habilidad del programador que los codifique. Por otra,
este análisis nos interesa especialmente cuando el algoritmo se aplica
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 14 de 91
a problema grandes. Casi siempre los problemas pequeños se pueden
resolver de cualquier forma, apareciendo las limitaciones al atacar
problemas grandes. No debe olvidarse que cualquier técnica de
ingeniería, si funciona, acaba aplicándose al problema más grande que
sea posible: las tecnologías de éxito, antes o después, acaban
llevándose al límite de sus posibilidades.
Las consideraciones anteriores nos llevan a estudiar el comportamiento
de un algoritmo cuando se fuerza el tamaño del problema al que se
aplica. Matemáticamente hablando, cuando N tiende a infinito. Es decir,
su comportamiento asintótico.
Sean "g(n)" diferentes funciones que determinan el uso de recursos.
Habrá funciones "g" de todos los colores. Lo que vamos a intentar es
identificar "familias" de funciones, usando como criterio de agrupación
su comportamiento asintótico.
A un conjunto de funciones que comparten un mismo comportamiento
asintótico le denominaremos un orden de complejidad'. Habitualmente
estos conjuntos se denominan O, existiendo una infinidad de ellos.
Para cada uno de estos conjuntos se suele identificar un miembro f(n)
que se utiliza como representante de la clase, hablándose del conjunto
de funciones "g" que son del orden de "f(n)", denotándose como:g IN
O(f(n))
Con frecuencia nos encontraremos con que no es necesario conocer el
comportamiento exacto, sino que basta conocer una cota superior, es
decir, alguna función que se comporte "aún peor".
La definición matemática de estos conjuntos debe ser muy cuidadosa para
involucrar ambos aspectos: identificación de una familia y posible
utilización como cota superior de otras funciones menos malas:
Dícese que el conjunto O(f(n)) es el de las funciones de orden de f(n),
que se define comoO(f(n))= {g: INTEGER -> REAL+ tales queexisten las constantes k y N0 tales
quepara todo N > N0, g(N) <= k*f(N) }
En palabras, O(f(n)) está formado por aquellas funciones g(n) que
crecen a un ritmo menor o igual que el de f(n).
De las funciones "g" que forman este conjunto O(f(n)) se dice
que "están dominadas asintóticamente" por "f", en el sentido de que
para N suficientemente grande, y salvo una constante multiplicativa
"k", f(n) es una cota superior de g(n).

1.8 ORDENES DE COMPLEJIDAD


Se dice que O(f(n)) define un "orden de complejidad". Escogeremos como
representante de este orden a la función f(n) más sencilla del mismo.
Así tendremos

Es más, se puede identificar una jerarquía de órdenes de complejidad


que coincide con el orden de la tabla anterior; jerarquía en el sentido
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 15 de 91
de que cada orden de complejidad superior tiene a los inferiores como
subconjuntos. Si un algoritmo A se puede demostrar de un cierto orden
O1, es cierto que también pertenece a todos los órdenes superiores (la
relación de orden çota superior de' es transitiva); pero en la práctica
lo útil es encontrar la "menor cota superior", es decir el menor orden
de complejidad que lo cubra.

1.9 IMPACTO PRÁCTICO


Para captar la importancia relativa de los órdenes de complejidad
conviene echar algunas cuentas.Sea un problema que sabemos resolver con
algoritmos de diferentes complejidades. Para compararlos entre sí,
supongamos que todos ellos requieren 1 hora de ordenador para resolver
un problema de tamaño N=100.
¿Qué ocurre si disponemos del doble de tiempo? Nótese que esto es lo
mismo que disponer del mismo tiempo en un ordenador el doble de
potente, y que el ritmo actual de progreso del hardware es exactamente
ese:"duplicación anual del número de instrucciones por segundo".
¿Qué ocurre si queremos resolver un problema de tamaño 2n?

Los algoritmos de complejidad O(n) y O(n log n) son los que muestran un
comportamiento más "natural": prácticamente a doble de tiempo, doble de
datos procesables.
Los algoritmos de complejidad logarítmica son un descubrimiento
fenomenal, pues en el doble de tiempo permiten atacar problemas
notablemente mayores, y para resolver un problema el doble de grande
sólo hace falta un poco más de tiempo (ni mucho menos el doble).
Los algoritmos de tipo polinómico no son una maravilla, y se enfrentan
con dificultad a problemas de tamaño creciente. La práctica viene a
decirnos que son el límite de lo "tratable".
Sobre la tratabilidad de los algoritmos de complejidad polinómica
habría mucho que hablar, y a veces semejante calificativo es puro
eufemismo. Mientras complejidades del orden O(n2) y O(n3) suelen ser
efectivamente abordables, prácticamente nadie acepta algoritmos de
orden O(n100), por muy polinómicos que sean. La frontera es imprecisa.
Cualquier algoritmo por encima de una complejidad polinómica se dice
"intratable" y sólo será aplicable a problemas ridículamente pequeños.A
la vista de lo anterior se comprende que los programadores busquen
algoritmos de complejidad lineal. Es un golpe de suerte encontrar algo
de complejidad logarítmica. Si se encuentran soluciones polinomiales,
se puede vivir con ellas; pero ante soluciones de complejidad
exponencial, más vale seguir buscando.
No obstante lo anterior ...

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 16 de 91
 ... si un programa se va a ejecutar muy pocas veces, los costes de
codificación y depuración son los que más importan, relegando la
complejidad a un papel secundario.
 ... si a un programa se le prevé larga vida, hay que pensar que le
tocará mantenerlo a otra persona y, por tanto, conviene tener en cuenta
su legibilidad, incluso a costa de la complejidad de los algoritmos
empleados.
 ... si podemos garantizar que un programa sólo va a trabajar sobre
datos pequeños (valores bajos de N), el orden de complejidad del
algoritmo que usemos suele ser irrelevante, pudiendo llegar a ser
incluso contraproducente.
Por ejemplo, si disponemos de dos algoritmos para el mismo problema,
con tiempos de ejecución respectivos:

asintóticamente, "f" es mejor algoritmo que "g"; pero esto es cierto a


partir de N > 100.
Si nuestro problema no va a tratar jamás problemas de tamaño mayor que
100, es mejor solución usar el algoritmo "g".El ejemplo anterior
muestra que las constantes que aparecen en las fórmulas para T(n), y
que desaparecen al calcular las funciones de complejidad, pueden ser
decisivas desde el punto de vista de ingeniería. Pueden darse incluso
ejemplos más dramáticos:

aún siendo dos algoritmos con idéntico comportamiento asintótico, es


obvio que el algoritmo "f" es siempre 100 veces más rápido que el "g" y
candidato primero a ser utilizado.

 ... Usualmente un programa de baja complejidad en cuanto a tiempo de


ejecución, suele conllevar un alto consumo de memoria; y viceversa. A
veces hay que sopesar ambos factores, quedándonos en algún punto de
compromiso.
 ... En problemas de cálculo numérico hay que tener en cuenta más
factores que su complejidad pura y dura, o incluso que su tiempo de
ejecución: queda por considerar la precisión del cálculo, el máximo
error introducido en cálculos intermedios, la estabilidad del
algoritmo, etc. etc.

1.10 PROPIEDADES DE LOS CONJUNTOS O(f)


No entraremos en muchas profundidades, ni en demostraciones, que se
pueden hallar en los libros especializados. No obstante, algo hay que
saber de cómo se trabaja con los conjuntos O() para poder evaluar los
algoritmos con los que nos encontremos.
Para simplificar la notación, usaremos O(f) para decir O(f(n))
Las primeras reglas sólo expresan matemáticamente el concepto de
jerarquía de órdenes de complejidad:
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 17 de 91
A. La relación de orden definida porf < g <=> f(n) IN O(g)
es reflexiva: f(n) IN O(f)
y transitiva: f(n) IN O(g) y g(n) IN O(h) => f(n) IN O(h)
B. f IN O(g) y g IN O(f) <=> O(f) = O(g)
Las siguientes propiedades se pueden utilizar como reglas para el
cálculo de órdenes de complejidad. Toda la maquinaria matemática para
el cálculo de límites se puede aplicar directamente:
C. Lim(n->inf)f(n)/g(n) = 0 => f IN O(g)
=> g NOT_IN O(f)
=> O(f) es subconjunto de O(g)
D. Lim(n->inf)f(n)/g(n) = k => f IN O(g)
=> g IN O(f)
=> O(f) = O(g)
E. Lim(n->inf)f(n)/g(n)= INF => f NOT_IN O(g)
=> g IN O(f)
=> O(f) es superconjunto de O(g)

Las que siguen son reglas habituales en el cálculo de límites:

F. Si f, g IN O(h) => f+g IN O(h)


G. Sea k una constante, f(n) IN O(g) => k*f(n) IN O(g)
H. Si f IN O(h1) y g IN O(h2) => f+g IN O(h1+h2)
I. Si f IN O(h1) y g IN O(h2) => f*g IN O(h1*h2)
J. Sean los reales 0 < a < b => O(na) es subconjunto de O(nb)
K. Sea P(n) un polinomio de grado k => P(n) IN O(nk)
L. Sean los reales a, b > 1 => O(loga) = O(logb)
La regla [L] nos permite olvidar la base en la que se calculan los
logaritmos en expresiones de complejidad.
La combinación de las reglas [K, G] es probablemente la más usada,
permitiendo de un plumazo olvidar todos los componentes de un
polinomio, menos su grado.

Por último, la regla [H] es la básica para analizar el concepto de


secuencia en un programa: la composición secuencial de dos trozos de
programa es de orden de complejidad el de la suma de sus partes.

1.11 REGLAS PRÁCTICAS


Aunque no existe una receta que siempre funcione para calcular la
complejidad de un algoritmo, si es posible tratar sistemáticamente una
gran cantidad de ellos, basándonos en que suelen estar bien
estructurados y siguen pautas uniformes.
Los algoritmos bien estructurados combinan las sentencias de alguna de
las formas siguientes
1. sentencias sencillas
2. secuencia (;)
3. decisión (if)
4. bucles
5. llamadas a procedimientos

1.11.1 SENTENCIAS SENCILLAS


Nos referimos a las sentencias de asignación, entrada/salida, etc.
siempre y cuando no trabajen sobre variables estructuradas cuyo tamaño
esté relacionado con el tamaño N del problema. La inmensa mayoría de

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 18 de 91
las sentencias de un algoritmo requieren un tiempo constante de
ejecución, siendo su complejidad O(1).

1.11.2 SECUENCIA(;)
La complejidad de una serie de elementos de un programa es del orden de
la suma de las complejidades individuales, aplicándose las operaciones
arriba expuestas.

1.11.3 DECISIÓN (if)


La condición suele ser de O(1), complejidad a sumar con la peor
posible, bien en la rama THEN, o bien en la rama ELSE. En decisiones
múltiples (ELSE IF, SWITCH CASE), se tomara la peor de las ramas.

1.11.4 BUCLES
En los bucles con contador explícito, podemos distinguir dos casos, que
el tamaño N forme parte de los límites o que no. Si el bucle se realiza
un número fijo de veces, independiente de N, entonces la repetición
sólo introduce una constante multiplicativa que puede absorberse.
Ejemplo:
for (int i= 0; i< K; i++) { algo_de_O(1) } => K*O(1) = O(1)
Si el tamaño N aparece como límite de iteraciones ...
Ejemplo:
for (int i= 0; i < N; i++) { algo_de_O(1) } => N * O(1) = O(n)

Ejemplo:
for (int i= 0; i < N; i++) {
for (int j= 0; j < N; j++) {
algo_de_O(1)
}
}
tendremos N * N * O(1) = O(n2)
Ejemplo:
for (int i= 0; i < N; i++) {
for (int j= 0; j < i; j++) {
algo_de_O(1)
}
}
el bucle exterior se realiza N veces, mientras que el interior se
realiza 1, 2, 3, ... N veces respectivamente. En total,
1 + 2 + 3 + ... + N = N*(1+N)/2 -> O(n2)

A veces aparecen bucles multiplicativos, donde la evolución de la


variable de control no es lineal (como en los casos anteriores)
Ejemplo:
c= 1;
while (c < N) {
algo_de_O(1)
c= 2*c;
}
El valor inicial de "c" es 1, siendo "2k" al cabo de "k" iteraciones. El
número de iteraciones es tal que2k >= N => k= eis (log2 (N)) [el entero
inmediato superior]y, por tanto, la complejidad del bucle es O(log n).
Ej.- c= N;
while (c > 1) {
algo_de_O(1)
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 19 de 91
c= c / 2; }
Un razonamiento análogo nos lleva a log2(N) iteraciones y, por tanto, a
un orden O(log n) de complejidad.
Ej.- for (int i= 0; i < N; i++) {
c= i;
while (c > 0) {
algo_de_O(1)
c= c/2;
}
}
tenemos un bucle interno de orden O(log n) que se ejecuta N veces,
luego el conjunto es de orden O(n log n)

1.11.5 LLAMADAS A PROCEDIMIENTOS


La complejidad de llamar a un procedimiento viene dada por la
complejidad del contenido del procedimiento en sí. El coste de llamar
no es sino una constante que podemos obviar inmediatamente dentro de
nuestros análisis asintóticos.
El cálculo de la complejidad asociada a un procedimiento puede
complicarse notablemente si se trata de procedimientos recursivos. Es
fácil que tengamos que aplicar técnicas propias de la matemática
discreta, tema que queda fuera de los límites de esta nota técnica.

1.11.6 Ejemplo: EVALUACIÓN DE UN POLINOMIO


Vamos a aplicar lo explicado hasta ahora a un problema de fácil
especificación: diseñar un programa para evaluar un polinomio P(x) de
grado N;

class Polinomio {
private double[] coeficientes;

Polinomio (double[] coeficientes) {


this.coeficientes= new double[coeficientes.length];
System.arraycopy(coeficientes, 0, this.coeficientes, 0,
coeficientes.length);
}

double evalua_1 (double x) {


double resultado= 0.0;
for (int termino= 0; termino < coeficientes.length; termino++) {
double xn= 1.0;
for (int j= 0; j < termino; j++)
xn*= x; // x elevado a n
resultado+= coeficientes[termino] * xn;
}
return resultado;
}
}
Como medida del tamaño tomaremos para N el grado del polinomio, que es
el número de coeficientes en C. Así pues, el bucle más exterior (1) se
ejecuta N veces. El bucle interior (2) se ejecuta, respectivamente
1 + 2 + 3 + ... + N veces = N*(1+N)/2 => O(n2)
Intuitivamente, sin embargo, este problema debería ser menos complejo,
pues repugna al sentido común que sea de una complejidad tan elevada.
Se puede ser más inteligente a la hora de evaluar la potencia xn:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 20 de 91
double evalua_2 (double x) {
double resultado= 0.0;
for (int termino= 0; termino < coeficientes.length; termino++) {
resultado+= coeficientes[termino] * potencia(x, termino);
}
return resultado;
}
private double potencia (double x, int n) {
if (n == 0)
return 1.0;
// si es potencia impar ...
if (n%2 == 1)
return x * potencia(x, n-1);
// si es potencia par ...
double t= potencia(x, n/2);
return t*t;
}

El análisis de la función Potencia es delicado, pues si el exponente es


par, el problema tiene una evolución logarítmica; mientras que si es
impar, su evolución es lineal. No obstante, como si "j" es impar
entonces "j-1" es par, el caso peor es que en la mitad de los casos
tengamos "j" impar y en la otra mitad sea par. El caso mejor, por
contra, es que siempre sea "j" par.
Un ejemplo de caso peor seria x31, que implica la siguiente serie para
j: 31 30 15 14 7 6 3 2 1
cuyo número de términos podemos acotar superiormente por
2 * eis (log2(j)),
donde eis(r) es el entero inmediatamente superior (este cálculo
responde al razonamiento de que en el caso mejor visitaremos
eis(log2(j)) valores pares de "j"; y en el caso peor podemos
encontrarnos con otros tantos números impares entremezclados).
Por tanto, la complejidad de Potencia es de orden O(log n).
Insertada la función Potencia en la función EvaluaPolinomio, la
complejidad compuesta es del orden O(n log n), al multiplicarse por N
un subalgoritmo de O(log n).
Así y todo, esto sigue resultando extravagante y excesivamente costoso.
En efecto, basta reconsiderar el algoritmo almacenando las potencias de
"X" ya calculadas para mejorarlo sensiblemente:

double evalua_3 (double x) {


double xn= 1.0;
double resultado= coeficientes[0];
for (int termino= 1; termino < coeficientes.length; termino++) {
xn*= x;
resultado+= coeficientes[termino] * xn;
}
return resultado;
}

que queda en un algoritmo de O(n).


Habiendo N coeficientes C distintos, es imposible encontrar ningún
algoritmo de un orden inferior de complejidad.
En cambio, si es posible encontrar otros algoritmos de idéntica
complejidad:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 21 de 91
double evalua_4 (double x) {
double resultado= 0.0;
for (int termino= coeficientes.length-1; termino >= 0; termino--)
{
resultado= resultado * x +
coeficientes[termino];
}
return resultado;
}

No obstante ser ambos algoritmos de idéntico orden de complejidad, cabe


resaltar que sus tiempos de ejecución serán notablemente distintos. En
efecto, mientras el último algoritmo ejecuta N multiplicaciones y N
sumas, el penúltimo requiere 2N multiplicaciones y N sumas. Si, como es
frecuente, el tiempo de ejecución es notablemente superior para
realizar una multiplicación, cabe razonar que el último algoritmo
ejecutará en la mitad de tiempo que el anterior.

1.11.7 MEDIDAS DE LABORATORIO


La siguiente tabla muestra algunas medidas de la eficacia de nuestros
algoritmos sobre una implementación en Java:

1.11.8 PROBLEMAS P, NP y NP-completos.


Hasta aquí hemos venido hablando de algoritmos. Cuando nos enfrentamos
a un problema concreto, habrá una serie de algoritmos aplicables. Se
suele decir que el orden de complejidad de un problema es el del mejor
algoritmo que se conozca para resolverlo. Así se clasifican los
problemas, y los estudios sobre algoritmos se aplican a la realidad.
Estos estudios han llevado a la constatación de que existen problemas
muy difíciles, problemas que desafían la utilización de los ordenadores
para resolverlos. En lo que sigue esbozaremos las clases de problemas
que hoy por hoy se escapan a un tratamiento informático.
Clase P.-
Los algoritmos de complejidad polinómica se dice que son tratables en
el sentido de que suelen ser abordables en la práctica. Los problemas
para los que se conocen algoritmos con esta complejidad se dice que
forman la clase P. Aquellos problemas para los que la mejor solución
que se conoce es de complejidad superior a la polinómica, se dice que
son problemas intratables. Sería muy interesante encontrar alguna
solución polinómica (o mejor) que permitiera abordarlos.
Clase NP.-
Algunos de estos problemas intratables pueden caracterizarse por el
curioso hecho de que puede aplicarse un algoritmo polinómico para
comprobar si una posible solución es válida o no. Esta característica
lleva a un método de resolución no determinista consistente en aplicar

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 22 de 91
heurísticos para obtener soluciones hipotéticas que se van desestimando
(o aceptando) a ritmo polinómico. Los problemas de esta clase se
denominan NP (la N de no-deterministas y la P de polinómicos).

Clase NP-completos.-
Se conoce una amplia variedad de problemas de tipo NP, de los cuales
destacan algunos de ellos de extrema complejidad. Gráficamente podemos
decir que algunos problemas se hayan en la "frontera externa" de la
clase NP. Son problemas NP, y son los peores problemas posibles de
clase NP. Estos problemas se caracterizan por ser todos "iguales" en el
sentido de que si se descubriera una solución P para alguno de ellos,
esta solución sería fácilmente aplicable a todos ellos. Actualmente hay
un premio de prestigio equivalente al Nobel reservado para el que
descubra semejante solución ... ¡y se duda seriamente de que alguien lo
consiga!
Es más, si se descubriera una solución para los problemas NP-completos,
esta sería aplicable a todos los problemas NP y, por tanto, la clase NP
desaparecería del mundo científico al carecerse de problemas de ese
tipo. Realmente, tras años de búsqueda exhaustiva de dicha solución, es
hecho ampliamente aceptado que no debe existir, aunque nadie ha
demostrado, todavía, la imposibilidad de su existencia.

1.11.9 CONCLUSIONES.
Antes de realizar un programa conviene elegir un buen algoritmo, donde
por bueno entendemos que utilice pocos recursos, siendo usualmente los
más importantes el tiempo que lleve ejecutarse y la cantidad de espacio
en memoria que requiera. Es engañoso pensar que todos los algoritmos
son "más o menos iguales" y confiar en nuestra habilidad como
programadores para convertir un mal algoritmo en un producto eficaz. Es
asimismo engañoso confiar en la creciente potencia de las máquinas y el
abaratamiento de las mismas como remedio de todos los problemas que
puedan aparecer.
En el análisis de algoritmos se considera usualmente el caso peor, si
bien a veces conviene analizar igualmente el caso mejor y hacer alguna
estimación sobre un caso promedio. Para independizarse de factores
coyunturales tales como el lenguaje de programación, la habilidad del
codificador, la máquina soporte, etc. se suele trabajar con un cálculo
asintótico que indica cómo se comporta el algoritmo para datos muy
grandes y salvo algún coeficiente multiplicativo. Para problemas
pequeños es cierto que casi todos los algoritmos son "más o menos
iguales", primando otros aspectos como esfuerzo de codificación,
legibilidad, etc. Los órdenes de complejidad sólo son importantes para
grandes problemas.

Ejercicios teoricos tema 1:


-------------------------------------------------------
1. ¿Qué es un algoritmo?
2. ¿Qué es la tecnologia?
3. Atendiento a lo visto en 1er año. ¿qué es un programa?
4. ¿Cuales son las metodologias empleadas en la programacion?
5. Enumera algunos recursos empleados en la informatica.
6. ¿Que entiendes por lenguaje de programacion?

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 23 de 91
7. Detallar los pasos del algoritmo " comprar samsung note4 en la
tienda Martinez Hnos de la Avda. Independencia".

Nota: los ejercicios teoricos se entregan hechos en papel.


los ejercicios practicos se entregan en la memoria USB.

TEMA 2:VECTORES Y ALGORITMOS DE ORDENACIÓN.

2.1 VECTOR
También llamado array,es una estructura de datos que contiene una
colección de datos del mismo tipo.
Ejemplo: las temperaturas mínimas de los últimos 30 días.
Un vector, es una colecciónhomogénea de elementos a los que se accede
mediante índice. Es estático (tamaño fijo)
Ejemplo, Vector B de 5 elementos con índice 0...4. sus elementos son de
tipo persona con campos de nombre y edad.
Para declarar un array, se utilizan corchetes para indicar que se trata
de un array y no de una simple variable del tipo especificado.

Ejemplos:

Tipoidentificador [];
Tipo [] identificador;
----------------------------
Tipoidentificador [][];
Tipo [][] identificador ;
B[]fila = new B[5];
fila[0]=txtNombrecontacto.getText();
fila[1]=txtApellidoscontacto.getText();
fila[2]=txtEmail.getText();
fila[3]=txtTelefono.getText();
fila[4]=txtDireccion.getText(); ();

NOTA: no es buena idea que el identificador del array termines en un digito.


Ejemplo: vector3

B[0] B[1] B[2] B[3] B[4]


{Pedro} {NZOO} {sule.com} {666000000} {Buena Esp.Z7}

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 24 de 91
En programación se denomina matriz, vector (de una sola dimensión) o
formación (en inglés array)
1. A una zona de almacenamiento contiguo que contiene una serie de
elementos del mismo tipo, los elementos de la matriz.
2. Desde el punto de vista lógico una matriz se puede ver como un
conjunto de elementos ordenados en fila (o filas y columnas si tuviera
dos dimensiones).
En principio, se puede considerar que todas las matrices son de una
dimensión, la dimensión principal, pero los elementos de dicha fila
pueden ser a su vez matrices (un proceso que puedeser recursivo), lo
que nos permite hablar de la existencia de matrices multidimensionales,
aunque las más fáciles de imaginar son los de una, dos y tres
dimensiones.
Estas estructuras de datos son adecuadas para situaciones en las que el
acceso a los datos se realice de forma aleatoria e impredecible. Por el
contrario, si los elementos pueden estar ordenados y se va a utilizar
acceso secuencial sería más adecuado utilizar una lista, ya que esta
estructura puede cambiar de tamaño fácilmente durante la ejecución de
un programa.

Vector (array unidimensional):


Tipo identificador[];
o bien
tipo[] identificador;
Dondetipoes el tipo de dato de los elementos del vector.identificador
es el identificador de la variable.

vector[índice]
En Java, el índice de la primera componente de un vector es siempre 0.
El tamaño del array puede obtenerse utilizando la propiedad
vector.length
Por tanto, el índice de la última componente esvector.length-1
Ejemplo:Float[]notas = new float[3]; // Es vector.length - 1
Ejemplo:
Float [ ] notas = new float [3]; // array de 3 elementos
(notas)// mat, hist, ingles

Los arrays en Java son suficientes para guardar tipos básicos de datos,
y objetos de una determinadaclase cuyo número conocemos de antemano.
Algunas veces deseamos guardar objetos en un arraypero no sabemos
cuántos objetos vamos a guardar. Una solución es la de crear un array
cuyadimensión sea más grande que el número de elementos que necesitamos
guardar. La clase Vectornos proporciona una solución alternativa a este
problema. Un vector es similar a un array, ladiferencia estriba en que
un vector crece automáticamente cuando alcanza la dimensión
inicialmáxima. Además, proporciona métodos adicionales para añadir,
eliminar elementos, e insertarelementos entre otros dos existentes.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 25 de 91
Vector (array bidimensional):
tipo identificador [][];
o bien
tipo [][] identificador;
<tipo> <nombre>[][]...
Int precios[80][10];
Inicialización:
<var> = new <tipo>[<num>][<num>]...
precios = new int[80][40] //array de 80x40
NOTA: No es una buena idea que el identificador del array termine en un dígito,
p.ej. vector3

Ejercicio teorico tema 2:

Tenemos el sigueinte vector:


String[]ciudades ={"Malabo","Bata","Luba","Riaba","Moka","Rio campo"};

1. ¿Cuál es el índice de Luba? Solución 2


2. ¿Cómo es la expresión para acceder a Luba? SoluciónCiudades[2]
3. ¿Cuál es el resultado de ciudades.length? Solución 6
4. ¿Cuál es el índice del último elemento?Solución 5
5. ¿Cuál es el valor de la expresión ciudades[3]? SoluciónRiaba
6. ¿Qué es un indice?
7. Diferencia entre vector y matriz.

2.2 CREAR UN VECTOR


Para usar la clase Vector en java, hemos de poner al principio del
archivo del código fuente la siguientesentencia import
import java.util.*;
Cuando creamos un vector u objeto de la clase Vector, podemos
especificar su dimensión inicial, y
cuanto crecerá si rebasamos dicha dimensión.
Ejemplo:
Vector =new tipo [elementos];
Entre corchetes se indica el tamaño del vector.
tipo debe coincidir con el tipo con el que se haya declarado el vector.
Vector debe ser una variable declarada como tipo[]
Ejemplos:
Float[] notas = new float[ALUMNOS];
Int[] temperaturas = new int[7];
Otro ejemplo:
Tenemos un vector con una dimensión inicial de 20 elementos. Si
rebasamos dicha dimensión yguardamos 21 elementos la dimensión del
vector crece a 25.
Al segundo constructor, solamente se le pasa la dimensión inicial.
Vector vector=new Vector(20);

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 26 de 91
Si se rebasa la dimensión inicial guardando 21 elementos, la dimensión
del vector se duplica. Elprogramador ha de tener cuidado con este
constructor, ya que si se pretende guardar un númerogrande de elementos
se tiene que especificar el incremento de la capacidad del vector, si
no sequiere desperdiciar inútilmente la memoria el ordenador.
Con el tercer constructor, se crea un vector cuya dimensión inicial es
10.
Vector vector=new Vector();
La dimensión del vector se duplica si se rebasa la dimensión inicial,
por ejemplo, cuando sepretende guardar once elementos.

Atención lenguaje C/C++:

Cout <<"la suma de los números en los vectores es" <<suma; // imprime
el resultado.Cout << " \n ";
Algunas aclaraciones:
\a carácter de alarma
\b retroceso
\n nueva línea
\r regreso de carro
\t tabulador horizontal
\\ diagonal invertida
\' apóstrofe
\" comillas

Notación
La representación de un elemento en un vector se suele hacer mediante
el identificador del vector seguido del índice entre corchetes,
paréntesis o llaves:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 27 de 91
2.3 VECTORES DINÁMICOS Y ESTÁTICOS
Lo habitual es que un vector tenga una cantidad fija de memoria
asignada, aunque dependiendo del tipo de vector y del lenguaje de
programación un vector podría tener una cantidad variable de datos. En
este caso, se les denomina vectores dinámicos, en oposición, a los
vectores con una cantidad fija de memoria asignada se los denomina
vectores estáticos.
El uso de vectores dinámicos requiere realizar una apropiada gestión de
memoria dinámica. Un uso incorrecto de los vectores dinámicos, o mejor
dicho, una mala gestión de la memoria dinámica, puede conducir a una
fuga de memoria. Al utilizar vectores dinámicos siempre habrá que
liberar la memoria utilizada cuando ésta ya no se vaya a seguir
utilizando.
Lenguajes más modernos y de más alto nivel, cuentan con un mecanismo
denominado recolector de basura (como es el caso de Java) que permiten
que el programa decida si debe liberar el espacio basándose en si se va
a utilizar en el futuro o no un determinado objeto.
Ejemplos en C

 Declaración en C/C++ de un vector estático.


int main(void)
{
int i, v[5]; // v[5] es un vector de 5 componentes (Indexación base-
cero)
for(i=0; i<5; i++)
{
v[i] = 0; // Asignamos un valor
printf("%d\n", v[i]);
printf("\n"); // Crea una nueva línea
}
return 0
}

 Declaración en C/C++ de un vector estático utilizando aritmética de


punteros.
Siendo el identificador del vector, un puntero constante que contiene
la dirección del comienzo del vector (vector[0], primer elemento)
int main(void)
{
int i, v[5]; // v[5] es un vector de 5 componentes (Indexación base-
cero)
for(i=0; i<5; i++)
{
*(v + i) = 0; // Asignamos un valor en la dirección (vector +
((índice * sizeof (int) cantidad de bytes de desplazamiento desde la
base.)
printf("%d\n", *(v + i));

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 28 de 91
printf("\n"); // Crea una nueva línea
}
return 0
}

2.3.1 DECLARACIÓN EN C++ DE UN VECTOR DE STL:


#include <vector>
vector<int> v; // Si no se especifica el tamaño inicial es 0
for(int i=0 ;i<5 ;i++)
{
v.push_back(2*i); // inserta un elemento al final del vector
}
El ejemplo anterior está hecho para el lenguaje C++. En C, para crear
vectores dinámicos se tendrían que utilizar las instrucciones malloc y
realloc para reservar memoria de forma dinámica (ver biblioteca
stdlib.h), y la función free para liberar la memoria utilizada.
Resultado:

2.3.2 VECTORES MULTIDIMENSIONALES


En Basic, Java y otros lenguajes es posible declarar matrices
multidimensionales, entendiéndolas como un vector de x dimensión. En
dichos casos en número de elementos del vector es el producto
resultante de cada dimensión.
<tipo><nombre>[][]...
int precios[][];
Inicialización:
<var> = new <tipo>[<num>][<num>]...
precios = new int[80][40] //array de 80x40

Ejercicios teoricos:

1. Crea un array tridimensional.


2. Crea un array bidimnsional.
3. Crear un vector de una sola dimension.
4. Crear un programa que escriba la numeracion de 1 hasta 10 dibujando
una matriz de 5x2.
5. Crear un programa que escriba la numeracion de 1 hasta 16 dibujando
una matriz de 4x4.
6. Crear un programa que escriba la numeracion de 1 hasta 30 dibujando
una matriz de 6x5.
7. Crear un programa que escriba la numeracion de 1 hasta 100 dibujando
una matriz de 10x10.
8. Crea un programa que imprima:
printf("Frase de prueba.\rotra frase encima\ny otras\b
mas\n");

2.4 ALGORITMOS DE ORDENACIÓN


Pone los elementos de un vector en una secuencia dada por una relación
de orden.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 29 de 91
B[0] B[1] B[2] B[3] B[4]
{Pergentino,10} {Juan, 20} {julia, 40} {Andrés, 12} {Eloísa, 16}

El vector anterior, ordenado por edad de forma creciente queda como


sigue:
B[0] B[1] B[2] B[3] B[4]
{Andrés, 12} {Eloísa, 16} {Juan, 20} {Pergentino, 10} {julia, 40}

Sobre los algoritmos de ordenación, vamos a distinguir entre lentos y


rápidos. La diferencia más grande es la eficiencia, es decir, como se
comportan al ordenar una gran entrada de datos, los lentos se comportan
en un orden cuadrático, es decir, O(n²), mientras que los algoritmos
rápidos se comportan, en un caso promedio en un orden logarítmico,
ósea, O (n log n).
Siempre que nos enseñan a ordenar un vector, o una lista, nos enseñan
los algoritmos más triviales y lógicos que cualquiera podría
implementar. Estos algoritmos son:

Ordenamiento por inserción.

Mínimo promedio Máximo


Comparaciones (n - 1) 𝑛2 + 𝑛 − 2 𝑛2 + 𝑛 − 2
4 2
Movimientos 2(n - 1) 𝑛2 + 9𝑛 − 10 𝑛2 + 3𝑛 − 4
4 4
Coste 0(n) 0(𝑛2 ) 0(𝑛2 )

Ejemplo:
B=[3, 2, -1, 5, 0, 2]
B(0) B(1) B(2) B(3) B(4) B(5)
3 2 -1 5 0 2

Paso 1 >>>> 2º respecto 1º [2, 3, -1, 5, 0, 2]


Paso 2 >>>> 3º respecto 1º y 2º [-1, 2, 3, 5, 0, 2]
Paso 3 >>>> 4º respecto 1º, 2º y 3º [-1, 2, 3, 5,0, 2]
Paso 4 >>>> 5º respecto 4 primeros[-1, 0, 2, 3, 5, 2]
Paso 5 >>>> 6º respecto 5 primeros [-1, 0, 2, 2, 3, 5]

Public void InsertionSort(int[]data)


{
Int i, temp, k;
For (i=1; i<data.length; i++)
{
Temp =data[i];
K=i -1;
While ((k >=0) && (data[k] > temp))
{

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 30 de 91
Data[k+i] = data[k];
k--;
}
Data[k+1] = temp;
}
}

Ordenamiento por selección.

Mínimo Promedio Máximo


Comparaciones 𝑛2 + 𝑛 𝑛2 + 𝑛 𝑛2 + 𝑛
2 2 2
Movimientos 3(n - 1) 𝑛(𝑙𝑛𝑛 − 𝑦) 3(𝑛 − 1) + 𝑛2 /4
𝑛2 + 𝑛
( 2
)

Coste 0(𝑛2 ) 0(𝑛2 ) 0(𝑛2 )

Ejemplo:
B=[3, 2, -1, 5, 0, 2]
Paso 1 >>>> 2º colocar 5 al final [2, 3, -1, 2, 0, 5]
Paso 2 >>>> 3º colocar 3 antes de 5 [0, 2, -1, 2, 3, 5]
Paso 3 >>>> 4º colocar 2 [0, 2, -1, 2, 3, 5]
Paso 4 >>>> 5º colocar 2 [0, -1,2, 2, 3, 5]
Paso 5 >>>> 6º colocar 0 [-1, 0, 2, 2, 3, 5]

Public void Selection Sort(int[] v)


{
Int i, j, k, p, buffer, limit = v.length -1;
For (k=0; k<limit; i++)
{
P = k;
For (i = k+1; i<= limit; i++)
If(v [i] < v[p] )
{
P=i;
}
If(p !=k)
{
Buffer =v[p];
V[p] = v[k];
V[k] = buffer;
}
}
}
}

Ordenamiento burbuja (Bubblesort).


El ordenamiento por burbuja es el algoritmo más sencillo probablemente.
Ideal para empezar. Consiste en ciclar repetidamente a través de la
lista, comparando elementos adyacentes de dosen dos. Si un elemento es

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 31 de 91
mayor que el que está en la siguiente posición se intercambian. Es un
algoritmo estable. El inconveniente es que es muy lento.

Ejemplo:
static void burbuja_lims(int v[])
{
int buffer;
int i, j;
for (i = 0; i<v.length; i++)
{
For (j = 0; j<i; j++)

If(v [i] < v[j] )


{
Buffer =v[j];
V[j] = v[i];
V[i] = buffer;
}
}
}
}

El ordenamiento por inserción técnicamente es la forma más lógica de


ordenar cualquier cosa para un humano, por ejemplo, una baraja de
cartas. Requiere O(n²).
Inicialmente se tiene un solo elemento, que obviamente es un conjunto
ordenado. Después, cuando hay k elementos ordenados de menor a mayor,
se toma el elemento k+1 y se compara con todos los elementos ya
ordenados, deteniéndose cuando se encuentra un elemento menor (todos
los elementos mayores han sido desplazados una posición a la derecha).
En este punto se inserta el elemento k+1 debiendo desplazarse los demás
elementos.
cpp static void insercion_lims(int T[],
int inicial, int final)
{
int i, j; int aux;
for (i = inicial + 1; i < final; i++)
{
j = i; while ((T[j] 0))
{
aux = T[j]; T[j] = T[j-1]; T[j-1] = aux; j--;
}
}
}

Por último, el ordenamiento por selección: Al igual que el algoritmo de


inserción es muy trivial, puesto que recorre el vector o la lista,
buscando el elemento más pequeño y colocandolo en la posición 0 del
vector, y así sucesivamente n-1 veces, tanto de grande como sea el
vector. Al igual que los algoritmos anteriores, requiere O(n²)
static void seleccion_lims(int T[], int inicial, int final)
{
int i, j, indice_menor;
int menor, aux;
for (i = inicial; i < final - 1; i++) {
indice_menor = i;
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 32 de 91
menor = T[i];
for (j = i; j < final; j++)
if (T[j] < menor) {
indice_menor = j;
menor = T[j];
}
aux = T[i];
T[i] = T[indice_menor];
T[indice_menor] = aux;
};
}
Ahora vamos a hablar de los rápidos que son los más interesantes:

 Algoritmo mergesort (ordenación por mezcla).


 Algoritmo Quicksort (ordenación rápida).
 Algoritmo Heapsort (ordenación por montículo).
 Algoritmo Shellsort.
Todos estos son muy interesantes en cuanto la implementación y a la
idea de organizar un vector, pero me voy a centrar en el algoritmo
mergesort y en el quicksort, que se basan en la técnica dividey
vencerás, esta técnica es de las más famosas en cuanto a multiplicación
de matrices puesto que la reduce de un n^3 a un n^2,78.
La técnicadividey vencerás:Se basa en coger un problema P y dividirlo
en subproblemas y resolver estos sin solapamientos, y luego
recursivamente unirlos para formar la solución del problema original.
El algoritmo mergesort se basa en esta técnica, con lo cual si n=1, ya
esta ordenado, pero si n>1, partimos el vector de elementos en dos o
más subcolecciones, ordenamos cada uno de ellos y luego la unimos en un
solo vector ordenado, es de orden O(n log n) . Pero, ¿ cómo hacemos la
partición?
Método 1: Primeros n-1 elementos en un conjunto A, y último en B,
ordenar A utilizando el esquema de división recursivamente, y B ya esta
ordenado, combinar A y B con un método inserta().
Método 2: Intentamos repartir los elementos de forma equitativa entre
los dos conjuntos, A toma n/k y B las sobrantes , ordenamos A y B
recursivamente, y por último combinamos A y B utilizando el proceso de
mezcla que combina dos listas en una.
cpp static void mergesort_lims(int T[], int inicial, int final) {
if (final - inicial < UMBRAL_MS)
{
insercion_lims(T, inicial, final);
}else {
int k = (final - inicial)/2;
int * U = new int [k - inicial + 1];
assert(U);
int l, l2;
for (l = 0, l2 = inicial; l < k; l++, l2++) U[l] = T[l2]; U[l] =
INT_MAX; int * V = new int [final - k + 1]; assert(V);
for (l = 0, l2 = k; l < final - k; l++, l2++) V[l] = T[l2]; V[l] =
INT_MAX;
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 33 de 91
mergesort_lims(U, 0, k);
mergesort_lims(V, 0, final - k);
fusion(T, inicial, final, U, V); d
delete [] U; delete [] V; }; } ```

El algoritmo quicksort ordena un vector V eligiendo entre sus elementos


un valor clave P que actúa como pivote, organiza tres secciones,
izquierda-P-derecha, todos los elementos a la izquierda deberán ser
menores a P, y los de la derecha mayores, los ordena sin tener que
hacer ningún tipo de mezcla para combinarlos, ¿cómo elegimos el
pivote?.
Método 1: Lo ideal sería que el pivote fuera la mediana del vector para
que las partes izquierda y derecha tuvieran el mismo tamaño.
Método 2: recorremos el vector con un índice i desde 0 a n-1, y otro
índice j inversamente y cuando se crucen, es decir, tenga el mismo
valor, ese se seleccionara como pivote.
cpp static void quicksort_lims(int T[],
int inicial, int final)
{
int k;
if (final - inicial < UMBRAL_QS)
{
insercion_lims(T, inicial, final);
}else { dividir_qs(T, inicial, final, k);
quicksort_lims(T, inicial, k);
quicksort_lims(T, k + 1, final); }; } ```

EJERCICIOS:
1. Crear una función o método de un vector con un array de 10
elementosasignando sus índices y valores.
2. Ordena por selección los elementos del array
Notas=[M,Z,I,L,O,R].
3. Ordena por quicksortlos elementos del array
Notas=[1,Z,8,L,0,O,R].
4. ¿Cómo se ordenan los elementos de un vector mediantemergesort?
5. ¿Cuál es la función de la técnica divide y vencerás?.
6. Diferencia entre los ordenamientos quicksort y mergesort.
7. Diferencia entre los ordenamientos quicksort y selección.

Puntuación: 1,4 puntos por apartado.

TEMA 3: LISTAS, PILAS Y COLAS

3.1 LISTAS
La lista es una estructura dinámica, ya que la longitud aumenta al
insertar y se reduce al suprimir un elemento.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 34 de 91
Nodo
Para la implementación de TDA’s como ser listas, pilas o colas, es
necesario utilizarnodos. Un nodo es una estructura que contiene un
elemento (El dato que se desea guardaren la estructura), y una
referencia a otro nodo. De esta forma es posible encadenar los
nodos pudiendo guardar una cantidad ilimitada de elementos.

Tipo Lista (o secuencia) colección homogénea de datos, ordenados según


su posición tal que cada elemento tiene un predecesor (excepto el
primero) y un único sucesor (excepto el ultimo). Las operaciones
asociadas son:
 Insertar(inserta un elemento x en la posición p de la lista)
 Localizar(localiza la posición p en la que se encuentra el dato
x)
 Recuperar (encuentra l elemento x que está en la posición p)
 Suprimir (elimina de la lista el elemento de la posición p)
 suprimirData (elimina de la lista cualquier aparición de elemento
x)
 anular (ocasiona que la lista se vacie la lista)
 primero (fin) proporciona el primer elemento de la lista
 imprimir, imprime todos los elementos de la lista en su
orden.

Ejemplo:

Supongamos la siguiente lista de números enteros:

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

(posición de 1 a 11)
La operación insertar (5, 4) inserta 5 en la posición 4, por tanto, la
lista resultante es:
2 0 -9 7 5 6 -5 8 -3 0 4
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 35 de 91
Ejemplo de una lista simple (código C++):
#include <stdio.h>
struct _agenda {
char nombre[20];
char telefono[12];
struct _agenda *siguiente;
};

struct _agenda *primero, *ultimo;

void mostrar_menu() {
printf("\n\nMenú:\n=====\n\n");
printf("1.- Añadir elementos\n");
printf("2.- Borrar elementos\n");
printf("3.- Mostrar lista\n");
printf("4.- Salir\n\n");
printf("Escoge una opción: ");
fflush(stdout);
}

/* Con esta función añadimos un elemento al final de la lista */


void anadir_elemento() {
struct _agenda *nuevo;

/* reservamos memoria para el nuevo elemento */


nuevo = (struct _agenda *) malloc (sizeof(struct _agenda));
if (nuevo==NULL) printf( "No hay memoria disponible!\n");

printf("\nNuevo elemento:\n");
printf("Nombre: "); fflush(stdout);
gets(nuevo->nombre);
printf("Teléfono: "); fflush(stdout);
gets(nuevo->telefono);

/* el campo siguiente va a ser NULL por ser el último elemento


de la lista */

nuevo->siguiente = NULL;

/* ahora metemos el nuevo elemento en la lista. lo situamos


al final de la lista */
/* comprobamos si la lista está vacía. si primero==NULL es que no
hay ningún elemento en la lista. también vale ultimo==NULL */

if (primero==NULL) {
printf( "Primer elemento\n");
primero = nuevo;
ultimo = nuevo;
} else {

/* el que hasta ahora era el último tiene que apuntar al


nuevo */
ultimo->siguiente = nuevo;
/* hacemos que el nuevo sea ahora el último */
ultimo = nuevo;
}
}

void mostrar_lista() {

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 36 de 91
struct _agenda *auxiliar; /* lo usamos para recorrer la lista */
int i;

i=0;
auxiliar = primero;
printf("\nMostrando la lista completa:\n");
while (auxiliar!=NULL) {
printf( "Nombre: %s, Telefono: %s\n",
auxiliar->nombre,auxiliar->telefono);
auxiliar = auxiliar->siguiente;
i++;
}
if (i==0) printf( "\nLa lista está vacía!!\n" );
}

int main() {
char opcion;

primero = (struct _agenda *) NULL;


ultimo = (struct _agenda *) NULL;
do {
mostrar_menu();
opcion = getch();
switch ( opcion ) {
case '1': anadir_elemento();
break;
case '2': printf("No disponible todavía!\n");
break;
case '3': mostrar_lista(primero);
break;
case '4': exit( 1 );
default: printf( "Opción no valida\n" );
break;
}
} while (opcion!='4');
}

-----------/* Ejercicio 1 : Crear una lista que almacene los n primeros


numeros enteros y calcular el menor, mayor, y el promedio.
1.
2. #include <iostream>
3. #include <stdlib.h>
4.
5. using namespace std;
6.
7. struct nodo{
8.
9. int nro;
10.
11. struct nodo *sgte;
12.
13. };
14.
15. typedef struct nodo *Tlista;
16.
17. /*-------------- Insertar siguiente Elemento-------------------*/
18. void insertarSgte(Tlista &lista, int valor)
19.
20. {

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 37 de 91
21. Tlista t, q = new(struct nodo);
22.
23. q->nro = valor;
24.
25. q->sgte = NULL;
26.
27. if(lista==NULL)
28. {
29. lista = q;
30. }
31.
32. else
33.
34. {
35. t = lista;
36. while(t->sgte!=NULL)
37. {
38. t = t->sgte;
39. }
40.
41. t->sgte = q;
42. }
43. }
44.
45. /*----------------------Mostrar Lista---------------------------*/
46. void reportarLista(Tlista lista)
47.
48. {
49. int i = 0;
50.
51. while(lista != NULL)
52.
53. {
54. cout <<' '<< i+1 <<") " << lista->nro << endl;
55.
56. lista = lista->sgte;
57.
58. i++;
59. }
60. }
61.
62. void calcularMayMenProm(Tlista,int mayor, int menor,int promedio,i
nt n){
63.
64. while(lista!=NULL){
65.
66. if(mayor<(lista->nro))
67.
68. mayor=lista->nro;
69.
70. if(menor>(lista->nro))
71.
72. menor=lista->nro;
73.
74. promedio+=lista->nro;
75.
76. lista=lista->sgte;
77. }
78.
79. promedio=promedio/n;

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 38 de 91
80.
81. cout<<endl<<"mayor:"<<mayor<<endl;
82.
83. cout<<endl<<"menor:"<<menor<<endl;
84.
85. cout<<endl<<"promedio:"<<promedio<<endl<<endl;
86. }
87.
88. /*---------------- Funcion Principal -------------------*/
89.
90. int main(void)
91.
92. {
93. Tlista lista = NULL;
94.
95. system("color 0a");
96.
97. cout<<"\n\n\t\t[ EJERCICIOS LISTAS SIMPLES ]\n";
98.
99. cout<<"\t\t-----------------------------\n\n";
100.
101. cout<<" EJERCICIO 1: Calcular mayor,menor y promedio de una
lista"<<endl<<endl;
102.
103. cout<<"\n Ingrese tamanio de lista: ";
104.
105. cin>>n;
106.
107. for(int i=1;i<=n;i++){
108.
109. insertarSgte(lista,i);
110. }
111. cout<<endl<<"Elementos de lista"<<endl;
112. reportarLista(lista);
113.
114. mayor=lista->nro;
115.
116. menor=lista->nro;
117.
118. promedio=lista->nro;
119.
120. lista=lista->sgte;
121.
122. calcularMayMenProm(lista, mayor, menor, promedio, n);
123.
124. system("pause");
125.
126. return 0;
127. }

3.2 PILA
La pila es una secuencia de elementos del mismo tipo en la que el
acceso a la misma se realiza por un único lugar denominado tope o cima
según la política LIFO (Last In, First Out)en primero en entrar es el
primero en salir:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 39 de 91
pop
push
Tope o cima

Vemos como el acceso a los elementos de la pila se realiza siempre


sobre un único extremo. Las operaciones que caracterizan la pila son
las de introducir un nuevo elemento sobre la cima (push) y la de
extraer el elemento situado en la cima (pop). Una forma de ver esta
estructura de datos es como una pila de libros en la que sólo se puede
coger el libro que está en la cima o apilar más libros sobre la misma,
pero los libros que sostienen la pila no son accesibles pues de otro
modo todo se desmoronaría.

 Pilas que contienen direcciones de retorno:


Cuando el código llama a un método, la dirección de la primera
instrucción que sigue a la llamada se inserta en la parte superior de
la pila de llamadas de métodos del thread actual. Cuando el método
llamado ejecuta la instrucción return, se saca la dirección de la parte
superior de la pila y la ejecución continúa en esa dirección. Si un
método llama a otro método, el comportamiento LIFO de la pila asegura
que la instrucción return del segundo método transfiere la ejecución al
primer método, y la del primer método transfiere la ejecución al código
que sigue al código que llamó al primer método. Como resultado una pila
"recuerda" las direcciones de retorno de los métodos llamados.
 Pilas que contienen todos los parámetros del método llamado y las
variables locales:
Cuando se llama a un método, la JVM reserva memoria cerca de la
dirección de retorno y almacena todos los parámetros del método llamado
y las variables locales de ese método. Si el método es un método de
ejemplar, uno de los parámetros que almacena en la pila es la
referencia this del objeto actual.
El interfaz en Java que define esta clase de objetos y sus métodos son
los siguientes:
Pila.java
Public interface Pila {
Public int longitud();
Public boolean esVacia();
Public void push(Object 0);
Public Object pop();
Public Object lprimero();
}

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 40 de 91
Un método de pila estática:
Public class PilaEstatica {
Public static void main (String [] args) {
Int dato;
Int pila [] = new int [5];
Scanner teclado = new Scanner (System.in);
For (int tope=0; tope<=4; tope++)
{
System.out.println ("proporciona datos para la pila");
Dato = teclado.nextInt();
Pila[tope]=dato;
}
For (int tope=4; tope>=0; tope --)
System.out.println("la pila tiene los siguientes datos: "+pila[tope]);
}
}

Veremos ahora dos implementaciones de pila, mediante arrays y listas


enlazadas.

Implementación de pilas mediante arrays.


Implementemos una Pila mediante un vector.
PilaArray.java
Public class PilaArray implements Pila {
Private int tope=1;
Private Object s[];
Private int capacidad =0;
Public PilaArray() {
This (1000);
}
Public PilaArray (int cap) {
capacidad =cap;
s=new Object[capacidad];
}
Public int longitud() {
Return (top+1);
}
Public boolean esVAcia(){
Return (top<0);
}
}
La dimensión de la pila se establece al crear la pila, mediante el
constructor. En el siguiente ejemplo creamos una pila con capacidad
para 125 elementos

PilaArray pila_de_ejemplo = new PilaArray(125);

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 41 de 91
Si hubiéramos usado el constructor por defecto se hubiera establecido
el tamaño de la pila en 1000 elementos.
Definimos un campo privado top para conocer en todo momento cuál es la
cima de la pila. De esta forma, si queremos añadir un nuevo elemento a
la pila (push) lo haremos en la posición siguiente a la que nos indica
este campo. Observe como sólo se inserta un nuevo elemento sobre la
cima cuando hay espacio suficiente, es decir la longitud de la pila es
menor que su capacidad. Primero se incrementa el valor del campo top y
después se inserta el elemento en la pila .

Public void push(Object 0){


If(longitud()<capacidad) s[++top]=0;
}

Para implementar las operaciones pop y primero se comprueba que la


lista no es vacía, en cuyo caso se devuelve un valor nulo (null). Para
el caso de pop se decrementa la variable top para eliminar el objeto de
la cima, mientras que para primero no, puesto que en este último sólo
se está consultando la cima.

Una pila, es una estructura de datos en la que el último elemento en


entrar es el primero en salir, por lo que también se denominan
estructuras LIFO (Last In, First Out).
En esta estructura sólo se tiene acceso a la cabeza o cima de la pila.
En este caso también se solicita un conjunto de datos de manera
iterativa a ingresar por teclado. Cuando se finaliza el ingreso, se
procede a mostrar los elementos de la pila y además se utiliza un
archivo binario para transformar la pila en memoria en una estructura
de datos persistente. Cada nodo de la pila, como en el caso anterior,
mantendrá una referencia al nodo siguiente haciendo posible el futuro
recorrido de la misma. Luego de finalizada la muestra de los elementos
de la pila, se consulta al usuario si se desea acceder a los datos de
la misma almacenados en el archivo binario, para lo cual, en el caso de
responder Si (S), se accede al archivo y se imprimen los registros de
manera secuencial.

Lenguaje C:
1. #include <conio.h>
2. #include <stdio.h>
3. #include <stdlib.h>
4. #include <ctype.h>
5.
6. struct productos
7. {
8. int codigo;

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 42 de 91
9. char nombre[50];
10. int existencia;
11. float precio;
12. };
13.
14. struct nodo
15. {
16. struct productos dato;
17. struct nodo *proximo;
18. };
19.
20. /* Declaracion de funciones */
21. void archivo(FILE *fp);
22. struct nodo *nuevonodo();
23. struct nodo *creapila(struct nodo *pri, struct productos x);
24. void muestra(struct nodo *pri, FILE *fp);
25. /* Fin de Declaracion */
26.
27. void main()
28. {
29. struct productos x;
30. struct nodo *pri=NULL;
31. FILE *fp;
32. char opcion; float auxiliar=0;
33. if((fp=fopen("C:\\Datos.txt","wb"))==NULL)
34. {
35. getch();
36. }
37. fseek(fp,0,2);
38. do
39. {
40. fflush(stdio);
41. printf("Ingrese el Codigo de Producto ");
42. scanf("%d",&x.codigo);
43. fflush(stdin);
44. printf("Ingrese Nombre de Producto ");
45. gets(x.nombre);
46. fflush(stdin);
47. printf("Ingrese la Existencia ");
48. scanf("%d",&x.existencia);
49. fflush(stdin);
50. printf("Ingrese el Precio ");
51. scanf("%f",&auxiliar); x.precio=auxiliar;
52. pri=creapila(pri,x);
53. fflush(stdin);
54. printf("Desea Ingresar otro Registro? (S/N) ");
55. scanf("%c",&opcion); opcion=toupper(opcion);
56. } while(opcion=='S');
57. muestra(pri,fp);
58. fflush(stdin);
59. printf("El contenido de la Pila se ha Guardado. Desea
Visualizarlo? (S/N)");
60. scanf("%c",&opcion); opcion=toupper(opcion);
61. if(opcion=='S') archivo(fp);
62. getch();
63. fclose(fp);
64. }
65.
66. struct nodo *creapila(struct nodo *pri, struct productos x)
67. {

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 43 de 91
68. struct nodo *p;
69. p=nuevonodo();
70. (*p).dato=x;
71. (*p).proximo=pri;
72. return p;
73. }
74.
75. struct nodo *nuevonodo()
76. {
77. struct nodo *p;
78. p=(struct nodo *)malloc(sizeof(struct nodo));
79. if(p==NULL)
80. {
81. printf("Memoria RAM Llena");
82. getch();
83. exit(0);
84. }
85. return p;
86. }
87.
88. void muestra(struct nodo *pri, FILE *fp)
89. {
90. clrscr();
91. struct nodo *aux;
92. while(pri!=NULL)
93. {
94. printf("Codigo: %d \n",(*pri).dato.codigo);
95. printf("Nombre: %s \n",(*pri).dato.nombre);
96. printf("Existencia: %d \n",(*pri).dato.existencia);
97. printf("Precio: %0.2f \n\n",(*pri).dato.precio);
98. fwrite(&pri->dato,sizeof(struct productos),1,fp);
99. aux=pri;
100. pri=(*pri).proximo;
101. free(aux);
102. }
103. }
104.
105. void archivo(FILE *fp)
106. {
107. struct productos x;
108. clrscr();
109. printf("Datos del Archivo:\n\n");
110. fread(&x,sizeof(struct productos),1,fp);
111. while(!feof(fp))
112. {
113. printf("Codigo: %d \n",x.codigo);
114. printf("Nombre: %s \n",x.nombre);
115. printf("Existencia: %d \n",x.existencia);
116. printf("Precio: %0.2f \n\n",x.precio);
117. fread(&x,sizeof(struct productos),1,fp);
118. }
119. printf("Fin de Archivo");

3.3 COLA
Una cola es una estructura de datos donde el primer elemento en entrar
es el primero en salir, también denominadas estructuras FIFO (First In,
First Out).

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 44 de 91
Una cola es un contenedor de objetos cuya inserción y eliminación sigue
la política FIFO.
En una Cola los elementos se añaden desde la parte de atrás o la parte
final de la cola, sin embargo la información se extrae desde el frente,
es decir, los elementos que se añadieron primero serán los primeros en
salir, esto se conoce como estructura FIFO (First In First Out).

2 4 7 9 12 21 25 31 31

Vector[ ];

El siguiente interfaz muestras las operaciones típicas para colas:


Cola.java

La siguiente es una posible implementación de colas mediante la


clase Nodo:

ColaEnlazada.java

Vemos como la clase Cola contiene dos campos, cola y cabecera que
apuntan al principio y al final de la cola. La cabecera la utilizaremos
para extraer elementos. Para insertar utilizaremos la cola.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 45 de 91
La operación encolar crea un nodo cuyo sucesor es nulo. Esto es porque
añadimos al final de la cola, es decir, donde apunta el campo cola. Si
la cola es vacía la cabecera y la cola apuntan al mismo objeto Nodo.

Para eliminar (desencolar) y para consultar (cabecera) se utiliza el


campo cabecera. Se extraen/consultan elementos de la cabeza de la cola.

Esta estructura de datos se puede definir como una lista enlazada con
acceso FIFO a la que sólo se tiene acceso al final de la lista para
meter elementos y al principio de esta para sacarlos.
En este .cpp se pide el ingreso por teclado de un conjunto de datos de
manera iterativa hasta que se ingrese la palabra "fin" cuando la
consola pida el dato "Nombre". Una vez finalizado el ingreso, y si hay
datos en la cola, se procede a mostrarlos navegando mediante las
estructuras (struct). Cada estructura contiene la posición de memoria
de la estructura siguiente en la cola, por lo que se las recorrerá
hasta el final y se las irá eliminando de la memoria (ya que
conceptualmente un nodo debe leerse de memoria una única vez).

Ejemplo de cola.cpp
Lenguaje C:
1. #include <stdio.h>
2. #include <conio.h>
3. #include <stdlib.h>
4. #include <string.h>
5. struct agenda
7. {
8. char nombre[50];
9. char telefono[25];
10. char mail[50];
11. };

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 46 de 91
13. struct nodo
14. {
15. struct agenda dato;
16. struct nodo *proximo;
17. };
19. struct nodo *nuevonodo();
20. int colavacia(struct nodo *);
21. struct nodo *creacola(struct nodo *, struct agenda);
22. void mostrar(struct nodo *);
23.
24. void main()

25. {
26. struct nodo *pri=NULL, *ult=NULL;
27. struct agenda x;
28. printf("Ingrese nombre: ");
29. gets(x.nombre);
30. while(strcmpi(x.nombre,"fin"))
31. {
32. printf("Ingrese telefono: ");
33. gets(x.telefono);
34. printf("Ingrese mail: ");
35. gets(x.mail);
36. ult=creacola(ult,x);
37. if(pri==NULL) pri=ult; // Si es la 1º pasada pongo en pri el
valor del primer nodo
38. printf("Ingrese nombre: ");
39. gets(x.nombre);
40. }
41. if(colavacia(pri)==1) { printf("No se ingresaron registros");
getch(); }
42. else mostrar(pri);
43. }
45. struct nodo *nuevonodo()
46. {

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 47 de 91
47. struct nodo *p;
48. p=(struct nodo *)malloc(sizeof(struct nodo));
49. if(p==NULL)
50. {
51. printf("Memoria RAM Llena");
52. getch();
53. exit(0);
54. }
55. return p;
56. }
58. struct nodo *creacola(struct nodo *ult, struct agenda x)

59. {
60. struct nodo *p;
61. p=nuevonodo();
62. (*p).dato=x;
63. (*p).proximo=NULL;
64. if(ult!=NULL) (*ult).proximo=p; // Si hay nodo anterior en //
prox pongo la dirección del nodo actual
65. return p;
66. }
68. int colavacia(struct nodo *pri)
69. {
70. if(pri==NULL) return 1;
71. else return 0;
72. }
74. void mostrar(struct nodo *pri)
75. {
76. struct nodo *aux;
77. while(pri!=NULL)
78. {
79. printf("Nombre: %s - Telefono: %s - Mail: %s
\n",pri>dato.nombre,pri->dato.telefono,pri->dato.mail);
80. aux=pri;
81. pri=(*pri).proximo;

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 48 de 91
82. free(aux);
83. }
84. getch();
}

Ejercicios en clase.

1. ¿Cuándo decimos que una cola es enlazada?


2. ¿una pila almacena los elementos de la misma manera que la lista?
3. Crea una lista, cola y pila.
4. ¿qué algoritmo usa la política FIFO y la LIFO?
5. Defina el funcionamiento de una lista simple y enlazada.

Ejercicios para casa


1. ¿Cuáles son las diferencias entre una lista y una pila?
2. ¿Cuáles son las diferencias entre una pila y una cola?
3. ¿Cómo se eliminan los elementos de una lista enlazada?
4. Crea ejemplo tipo grafico de pila y lista enlazada aplicando
sus políticas.
5. ¿Qué ocurre Cuando el método llamado ejecuta la instrucción
return?
6. ¿Qué es tope o cima de la pila Y como se identifica?
7. Cita las implementaciones de una pila.
8. Crea una pila de 6 nodos y 4 elementos indicando su top

TEMA 4: GRAFOS Y ARBOLES. REPRESENTACIÓN Y RECORRIDO GRAFOS.


4.1.1 REPRESENTACIÓN.
Un grafo es un conjunto de nodos unidos por un conjunto de líneas o
flechas. Por lo general, los nodos son entes de procesamiento o
estructuras que contienen algún tipo de información y las líneas o
flechas son conexiones o relaciones entre estos entes. Si se utilizan
flechas para conectar los nodos decimos que el grafo es dirigido
(también llamado dígrafo) porque las relaciones entre los nodos tienen
una dirección. En caso contrario el grafo es no dirigido. En cualquiera
de los dos casos, bien sea que se utilicen líneas o flechas, a estas
relaciones se les puede llamar simplemente aristas. Frecuentemente las
aristas también tienen algún tipo de información asociada (distancia,
costo, confiabilidad, etc.), en cuyo caso estamos en presencia de un
grafo pesado.
Las secuencias de aristas forman caminos o ciclos. Un ciclo es un
camino que termina en el mismo nodo donde comenzó. Si el camino recorre

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 49 de 91
todos los nodos del grafo es llamado tour. El número de aristas en un
camino es la longitud del camino.

Se dice que un grafo es conexo si se puede llegar desde cualquier nodo


hasta cualquier otro mediante un camino. De lo contrario no es conexo,
pero puede dividirse en componentes conexas, que son subconjuntos de
nodos y aristas del grafo original que si son conexos. Un grafo conexo
sin ciclos es llamado un árbol.

Estos son apenas unos cuantos conceptos de lo que se conoce como la


Teoría de Grafos. El objetivo de estas notas no es cubrir por completo
dicha teoría sino enfocarnos en la implementación de este tipo de
estructuras y las operaciones y algoritmos más comunes e importantes
que se aplican sobre las mismas.

Un grafo es una pareja de G=<N,A> en donde N es un conjunto de nodos y


A es un conjunto de aristas que se denotan como parejas de nodos: par
ordenado (a, b) para grafos dirigidos, y conjunto {a, b} para grafos no
dirigidos.

Existen dos formas de representar un grafo en un ordenador:

A)matriz de adyacencia:
Tipo grafoadya = registro
Nodo: vector [ n ] de información
Adyacencia: matriz [n, n] de booleanos

Si una arista(i, j) existe, entonces adyacente [i,j] = true. En caso


contrario es false. En un grafo no dirigido, la matriz adyacente es
necesariamente simétrica
NOTA: DEFINICION SIMETRIA
Correspondencia en tamaño, forma, disposición entre las partes de un todo.

Ejemplo de representación con matriz de adyacencia.

Vector de nodos

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 50 de 91
Malabo Baney Bata Mbini Niefang

Matriz de adyacencia
Malabo Baney Bata Mbini Niefang
Malabo 0 1 1 0 0
Baney 1 0 0 0 0
Bata 1 0 0 1 1
Mbini 0 0 1 0 0
Niefang 0 0 1 0 0

B)lista de adyacencia:
Tipo grafolista = vector[n] de registro
Nodo: información
Vecinos: lista de nodos
A cada nodo i, se le asocia una lista formada por todos los nodos j
tales que existe a arista (i,j).
Si el numero de aristas es pequeño, esta representación utiliza menos
espacio. También se analiza rápidamente todos los vecinos de un nodo.
Sin embargo, es menos eficiente el determinar si existe o no una
conexión directa entre nodos.

Ejemplo de las ciudades que se enlazan directamente con otras:

Malabo Bane Bata


y

Baney Malab
o

Bata Mbin Niefan


i g

Mbini
Nota: Los elementos de la lista de adyacencia son referencias a los nodos del vector de nodos.

Niefang

4.1.2 RECORRIDO DE GRAFOS.


Ejemplos de problemas
4.1.2.1 EL CICLO EULERIANO
La ciudad deKönigsberg está atravesada por un rio que tiene 2 islas y 7
puentes como muestra la figura 1. Se pregunta si es posible partir del
sector A y, haciendo una caminata, pasar por cada puente una sola vez
volviendo al punto de partida. En el grafo de la figura 2 el problema
se traduce en partir de A y recorrer las 7 ramas sin repetir ninguna y
volver a A (ciclo euleriano). Este problema fue encarado por Euler en
1736 y es el origen de la teoría de grafos.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 51 de 91
figura 1

A figura 2

C D
AC D

4.1.2.2 EL CICLO HAMILTONIANO.

A un dodecaedro, cuerpo solido regular con doce caras pentagonales, se


la ha quitado una cara y se lo ha aplastado en el plano como muestra la

Imaginemos a los vértices de esta figura como ciudades y a las aristas


como tramos de caminos entre dos ciudades. Se pregunta si hay un camino
formado de tramos que partiendo de una ciudad visite todas las ciudades
una sola vez volviendo a la ciudad de partida (ciclo hamiltoniano)

Ejercicios

1. Crea un vector y una matriz de adyacencia de los 5 barrios de la


ciudad de Malabo.
2. Crea un grafo con representación matriz de adyacencia de 5
ciudades de la región continental.
3. Diferencia entre un ciclos hamiltoniano y euleriano.
4. ¿En qué consiste el ciclo euleriano?
5. Crea un grafo del ciclo euleriano.
6. ¿En qué cosiste el ciclo hamiltoniano?
7. crea un dígrafo.
8. Crea un grafo del ciclo hamiltoniano.
9. Diferencia entre es arista yun tour.
/*

Practicas de los grafos en java y c++

Ejemplo de un grafo
-----------------------------------------
Matriz de adyacencia:
las aristas del grafo se guardan en una matriz, indicando si un
nodo tiene enlace directo con otro.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 52 de 91
/*
O\D 0 1 2 3 4 5
0 0 1 0 1 0 0
1 0 0 1 0 1 0
2 0 0 0 0 1 0
3 0 1 0 0 0 0
4 0 0 0 1 0 1
5 0 0 1 0 0 0

En C++, podemos usar un doble array para guardar la matriz de antes:


*/
int A[6][6];
//inicializar todo a 0
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
A[i][j] = 0;

//1 si hay enlace origen -> destino


A[0][1] = 1;
A[1][2] = 0;
A[1][4] = 1;
A[2][4] = 1;
A[3][1] = 1;
A[4][3] = 1;
A[4][5] = 1;
A[5][2] = 1;

/* Lista de adyacencia: para cada nodo, sólo se guardan sus vecinos. */

0 -> 1 3
1 -> 2 4
2 -> 4
3 -> 1
4 -> 3 5
5 -> 2
// En C++, podemos usar un vector de vectores para guardar la lista
anterior:
vector<vector<int>> A(6);
//para cada posición origen añadir vecino
A[0].push_back(1);
A[0].push_back(3);
A[1].push_back(2);
A[1].push_back(4);
A[2].push_back(4);
A[3].push_back(1);
A[4].push_back(3);
A[4].push_back(5);
A[5].push_back(2);
// Lista de aristas: se guarda una lista de todas las aristas del
grafo.
(0,1) (0,3) (1,2) (1,4) (2,4) (3,1) (4,3) (4,5) (5,2)

// En C++, podemos usar un vector de pares de enteros para guardar la


lista:
//librería para utilizar pair
#include <utility>;
//vector de aristas
vector<pair<int,int>> A;

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 53 de 91
//insertamos las aristas en el vector
A.push_back(make_pair(0, 1));
A.push_back(make_pair(0, 3));
A.push_back(make_pair(1, 2));
A.push_back(make_pair(1, 4));
A.push_back(make_pair(2, 4));
A.push_back(make_pair(3, 1));
A.push_back(make_pair(4, 3));
A.push_back(make_pair(4, 5));
A.push_back(make_pair(5, 2));

EJERCICIOS MATRICES Y LISTAS

crear el grafo que tiene la siguiente matriz y Lista de adyacencia:


a) matriz de adyacencia
O\D 0 1 2 3 4 5
0 0 1 0 1 0 0
1 0 0 1 0 1 0
2 0 0 0 0 1 0
3 0 1 0 0 0 0
4 0 0 0 1 0 1
5 0 0 1 0 0 0

b) Lista de adyacencia:
0 -> 1 3
1 -> 2 4
2 -> 4
3 -> 1
4 -> 3 5
5 -> 2

4.2 ÁRBOLES

Un árbol es un grafo aciclico, conexo y no dirigido. Igualmente, se


puede definir como un grafo no dirigido en el cual existe exactamente
un camino entre todo par de nodos dado.
Por tanto, se puede utilizar las mismas implementaciones que para los
grafos.
Un Árbol consiste en un nodo (r, denominado nodo raíz) y una lista o
conjunto de subárboles (A1, A2, .. Ak).
 Si el orden de los subárboles importa, entonces forman una lista,
y se denomina árbol ordenado (por defecto un árbol se supone que es
ordenado). En caso contrario los subárboles forman un conjunto, y se
denomina árbol no ordenado.
 Se definen como nodos hijos de r a los nodos raíces de los
subárboles A1, A2, .. Ak
 Si b es un nodo hijo de a entonces a es el nodo padre de b.
 Un nodo puede tener cero o más hijos, y uno o ningún padre.
Elúnico nodo que no tiene padre es el nodo raíz del árbol.
 Un nodo sin hijos se denomina nodo hoja o externo. En caso
contrario se denomina nodo interno.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 54 de 91
 Se define un camino en un árbol como cualquier secuencia denodos
del árbol, n1 ... np, que cumpla que cada nodo es padre delsiguiente en
la secuencia (es decir, que ni es el padre de ni+1). Lalongitud del
camino se define como el número de nodos de la secuencia menos uno (p-
1).
 Los descendientes de un nodo(c en el diagrama) son aquellos nodos
accesibles por un camino que comience en el nodo.
 Los ascendientes de un nodo(f en el diagrama) son los nodos
del camino que va desde la raíz a él.

Un árbol es un nodo que contiene un elemento, de tipo genérico a, y una


lista de árboles.
El único nodo distinguido es el nodo raíz.

4.2.1 REPRESENTACIONES
 Las representaciones del TAD Directorio (elementos con
relación de jerarquía) suelen ser representaciones enlazadas,
donde cada nodo almacena enlaces al nodo padre y/o a los nodos
hijos.
 El único nodo distinguido es el nodo raíz.
 El método más habitual de realizar las operaciones es
mediante un iterador (cursor) que marca un nodo concreto que
sirve de referencia.
 Otra posibilidad es indicar un nodo concreto mediante el
camino de la raíz a ese nodo.
Padre - primer hijo - hermano
 Los nodos tienen un número fijo de enlaces: al padre, al primer
hijo y al siguiente hermano.
 La lista de hijos está representada como una lista enlazada.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 55 de 91
Árbol binario: Es un árbol que o bien está vacío (sin contenido)o bien
consta de un nodo raíz con dos subárboles binarios,denominados
izquierdo y derecho.
 La existencia de árboles vacíos es una convención para que no
exista ambigüedad al identificar el subárbol izquierdo y derecho. Se
representa por un cuadrado.
 La altura de un árbol vacío es -1 a
 Cada nodo puede tener 0 hijos (subárbol izquierdo y derecho
vacíos), 1 hijo (algún subárbol vacío) o 2 hijos.

4.2.2 ALTURA
 Se define la altura de un nodo en un árbol como la longitud
delcamino más largo que comienza en el nodo y termina en una hoja.
 La altura de un nodo hoja es 0.
 La altura de un nodo es igual a la mayoraltura de sus hijos + 1.
 La altura de un árbol se define como la altura de la raíz.
 La altura de un árbol determina la eficiencia de la mayoría de
operaciones definidas sobre árboles.

4.2.3 PROFUNDIDAD
 Se define la profundidad de un nodo en un arbol como la longitud
del camino (único) que comienza en la raíz y termina en el nodo.
También se denomina nivel.
 La profundidad de la raiz es 0.
 La profundidad de un nodo es igual a la profundidad de supadre + 1

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 56 de 91
4.2.4 RECORRIDO DE ÁRBOLES
Un árbol tiene tres tipos de recorridos: Preorden, Inorden, Postorden.
 Preorden: Se pasa por la raíz y luego se recorre en preorden cada
uno de los subárboles. Recursivo.
 Postorden: Se recorre en postorden cada uno de los subárboles y
luego se pasa por la raiz. Recursivo.
 Inorden: Se recorre en inorden el primer subárbol (si existe). Se
pasa por la raíz y por último se recorre en inorden cada uno de los
subárboles restantes. Tiene sentido fundamentalmente en árboles
binarios. Recursivo.
 Por Niveles: Se etiquetan los nodos según su profundidad (nivel).
Se recorren ordenados de menor a mayor nivel, a igualdad de nivel se
recorren de izquierda a derecha.
 No recursivo: Se introduce el raiz en una cola y se entra en un
bucleen el que se extrae de la cola un nodo, se recorre su elemento y
se insertan sus hijos en la cola

Ejemplo de recorridos en arboles binarios.

Otro ejemplo:

 En general, la diferencia entre preorden, inorden y postorden es cuándo


se recorre la raíz. En los tres, se recorre primero el sub-árbol
izquierdo y luego el derecho.

 Preorden (antes), inorden (en medio), postorden (después).


PREORDEN: (raíz, izquierdo, derecho).

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 57 de 91
Para recorrer un árbol binario no vacío en preorden, hay que realizar
las siguientes operaciones recursivamente en cada nodo, comenzando con
el nodo de raíz:
1. Visite la raíz
2. Atraviese el sub-árbol izquierdo
3. Atraviese el sub-árbol derecho

INORDEN: (izquierdo, raíz, derecho). Para recorrer un árbol binario no


vacío en inorden (simétrico),hay que realizar las siguientes
operaciones recursivamente en cada nodo:

1. Atraviese el sub-árbol izquierdo


2. Visite la raíz
3. Atraviese el sub-árbol derecho.

POSTORDEN: (izquierdo, derecho, raíz). Para recorrer un árbol binario


no vacío en postorden, hay que realizar las siguientes operaciones
recursivamente en cada nodo:
1. Atraviese el sub-árbol izquierdo
2. Atraviese el sub-árbol derecho
3. Visite la raíz

 Recorrido en profundidad.
Se trata de recorrer el grafo visitando primero los hijos que los
hermanos.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 58 de 91
 Recorrido en anchura.
Recorrer el grafo visitando primero los hermanos que los hijos.

"En Ciencias de la Computación, Búsqueda en anchura (en inglés BFS -


Breadth First Search) es un algoritmo para recorrer o buscar elementos
en un grafo (usado frecuentemente sobre árboles). Intuitivamente, se
comienza en la raíz (eligiendo algún nodo como elemento raíz en el caso
de un grafo) y se exploran todos los vecinos de este nodo. A
continuación para cada uno de los vecinos se exploran sus respectivos
vecinos adyacentes, y así hasta que se recorra todo el árbol."
Un recorrido en anchura se refiere a recorrer un grafo por niveles, es
decir, partiendo de un nodo inicial recorro todos sus vecinos,
posteriormente los vecinos de los vecinos hasta que todos los nodos
hayan sido visitados.

Ejercicios para casa:


1. Dar la solución del recorrido del este árbol en PREORDEN.

Solución: A-B-E-I-F-C-G-J-K-H-D

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 59 de 91
2. Crea un árbol de 5 nodos e indica el INORDENsu recorrido asc.
3. Trace el recorrido descendente de este árbol.

4. ¿Cómo está representada la lista de los hijos de un árbol?


5. ¿Qué nodo es distinguido en un árbol?
6. ¿Cómo se recorre la igualdad de nivel de un árbol?
7. ¿qué es un recorrido en anchura?
8. Recorraen anchura y profundidadelsiguienteárbol.

En anchura En profundidad

9. Diferencia entre los recorridos en anchura y profundidad.

4.2.5 VARIANTES DE ÁRBOLES BINARIOS


 Árbol estricto: Si un subárbol está vacío, el otro también. Cada
nodo puede tener 0 ó 2 hijos.
 Árbol lleno: Árbol estricto donde en cada nodo la altura del
subárbol izquierdo es igual a la del derecho, y ambos subárboles
son árboles llenos.
 Árbol completo: Árbol lleno hasta el penúltimo nivel. En el último
nivel los nodos están agrupados a la izquierda.

4.2.5.1 ÁRBOLES COMPLETOS


Los árboles llenos son los árboles con máximo número de nodos (n) para
una altura (h) dada. Se cumple que n = 2h+1-1.
El número de nodos de un árbol lleno sólo puede ser una potencia de dos
menos uno: 1, 3, 7, 15, 31, ….
Los árboles completos pueden almacenar cualquier número de nodos y se
sigue cumpliendo que su altura es proporcional al logaritmo del número
de nodos: h O(log n).
Además tienen la propiedad de que conocido el recorrido por niveles del
árbol es posible reconstruirle:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 60 de 91
Es posible almacenar un árbol completo en un vector en el orden dado
por su recorrido por niveles, y a partir del índice de un elemento en
el vector conocer el índice de su nodo padre y los de sus nodos hijos:

Es posible almacenar un árbol completo en un vector en el orden dado


por su recorrido por niveles, y a partir del índice de un elemento en
el vector conocer el índice de su nodo padre y los de sus nodos hijos:

Ejemplo de un arbol binario en java:


/*
arbol binario en java por profe H. Pergentino E.
------------------------------------------------
UNGE
Dept. informatica gest
*/

public class ArbolBin{


NodoB Padre;
NodoB Raiz;

//Constructor
public ArbolBin(){
Raiz = null;
}

//Insercion de un elemento en el arbol


public void insertaNodo(int Elem){
if(Raiz == null)
Raiz = new NodoB(Elem);
else
Raiz.insertar(Elem);
}

//recorrido en Preorden Recursivo del arbol


public void preorden (NodoB Nodo){
if(Nodo == null)
return 0;
else{
System.out.print (Nodo.dato + " ");
preorden (Nodo.Hizq);
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 61 de 91
preorden (Nodo.Hder);
}
}

//recorrido en PostOrden recursivo del arbol


public void postOrden (NodoB Nodo){
if(Nodo == null)
return 0;
else{
postOrden (Nodo.Hizq);
postOrden (Nodo.Hder);
System.out.print (Nodo.dato + " ");
}
}

//recorrido en Inorden Recursivo del arbol


public void inorden (NodoB Nodo){
if(Nodo == null)
return;
else{
inorden (Nodo.Hizq);
System.out.print(Nodo.dato + " ");
inorden (Nodo.Hder);
}
}

//cantidad de niveles que posee el arbol


public int altura (NodoB Nodo){
if (Nodo==null)
return -1;
else
return 1+Math.max(altura(Nodo.Hizq),altura(Nodo.Hder));
}
//cantidad de elementos que posee el arbol
public int tamaño (NodoB Nodo){
if (Nodo==null)
return 0;
else
return 1+tamaño(Nodo.Hizq)+tamaño(Nodo.Hder);
}
//Busca un elemento en el arbol
public void buscar (int Elem, NodoB A){
if(A == null | A.dato == Elem){
System.out.print(A.dato + " ");
return;
}
else{
if(Elem>A.dato)
buscar(Elem, A.Hder);
else
buscar( Elem, A.Hizq);
}
}

public static void main (String[]args)


{
ArbolBin A = new ArbolBin();
A.insertaNodo(1);
A.insertaNodo(3);

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 62 de 91
A.insertaNodo(2);
A.insertaNodo(23);
A.insertaNodo(24);
System.out.print("El recorrido en Preorden es: ");
A.preorden (A.Raiz);
System.out.println();
System.out.print("El recorrido en Inorden es: ");
A.inorden (A.Raiz);
System.out.println();
System.out.print("El recorrido en Postorden es: ");
A.postOrden (A.Raiz);
System.out.println();
System.out.println("La altura del arbol es: " + A.altura
(A.Raiz));
System.out.println("La cantidad de \"nodos\" que posee el
arbol es: " + A.tamaño(A.Raiz));
}
}
/*-------------------------------------------------------------
creacion del nodo del arbol
*/

public class NodoB{


int dato;
NodoB Hizq, Hder;

//Constructores
NodoB (int Elem){
dato = Elem;
NodoB Hizq, Hder = null;
}
NodoB (){
NodoB Hizq, Hder = null;
}
public void insertar(int Elem){
if(Elem < dato){
if (Hizq == null)
Hizq = new NodoB(Elem);
else
Hizq.insertar(Elem);
}
else{
if (Elem > dato){
if (Hder == null)
Hder = new NodoB(Elem);
else
Hder.insertar(Elem);
}
}
}
}

TEMA 5. OTRAS ESTRUCTURAS DE DATOS: MONTÍCULO (HEAP) Y CONJUNTOS.


5.1 MONTÍCULO (HEAP)
Un montículo es el método de ordenación de elementos del árbol binarioy
posee la siguiente característica:para todo nodo del árbol se debe
cumplir que su valor sea mayor o igual al de cualquiera de sus hijos.
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 63 de 91
Para representar un montículo en un array lineal:
1. El nodo k se almacena en la posición k correspondiente del array.
2. El hijo izquierdo del nodo k, se almacena en la posición 2*k.
3. El hijo derecho del nodo k, se almacena en la posición 2*k+1.
La estructura de datos del Montículo es un arreglo de objetos que puede
ser visto como un árbol binario con raíz, cuyos nodos pertenecen a un
conjunto totalmente ordenado, y tal que cumple las siguientes dos
propiedades:
a) Propiedad de orden: La raíz de cada subárbol es mayor o igual que
cualquiera de susnodos restantes.
b) Propiedad de forma: La longitud de toda rama es h o h − 1, donde “h”
es la altura del árbol.
Además, si una rama termina en una hoja a la derecha de otra hoja, esta
ultima de altura h − 1,la primera debe ser de altura h − 1.

Gráficamente:

Fig. 1: Representación de un montículo

Utilizaremos un array para representar un árbol binario.

Un arreglo A que representa un montículo es un objeto con 2 atributos:


 Length [A]: número de elementos en el arreglo.
 Heap-zise [A]: número de elementos en el montículo almacenados
dentro de A

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 64 de 91
A[...heap-zise[A]] es el montículo representado.

5.1.1 MÉTODO DE ORDENAMIENTO HEAPSORT.


Se toman las mejores características de los dos algoritmos de
ordenamiento basados encomparación (MergeSort e InsertionSort) para
crear un nuevo algoritmo llamado HeapSort.
Este algoritmo está basado en la estructura de montículo.
El método de ordenación HeapSort es también conocido con el nombre
“montículo” en elmundo de habla hispana. Su nombre se debe a su autor
J. W. Williams quien lo bautizo así.
Es el más eficiente de los métodos de ordenación que trabajan con
árboles.
La idea central de este algoritmo consiste en lo siguiente:
 Construir un montículo.
 Eliminar la raíz del montículo en forma repetida.
El método de ordenación se puede describir con los siguientes pasos:
1. Construir un montículo inicial con todos los elementos del vector
A[1], A[2], …., A[n]
2. Intercambiar los valores de A[1] y A[n] (siempre se queda el
máximo en el extremo)
3. Reconstruir el montículo con los elementos A[1], A[2],……, A[n-1]
4. Intercambiar los valores de A[1] y A[n-1]
5. Reconstruir el montículo con los elementos A[1], A[2],……, A[n-2]
Este es un proceso iterativo que partiendo de un montículo inicial,
repite intercambiar los extremos, decrementar en 1 la posición del
extremo superior y reconstruir el montículo del nuevo vector.
Lo expresamos en forma algorítmicaasí:
Ordenación Heapsort (Vector, N)
Debe construirse un montículo inicial (Vector, N)desde k = N hasta 2
hacer intercambio (Vector[1], Vector[k])
construir montículo (Vector, 1, k-1)
Entonces de acuerdo al algoritmo debemos considerar dos problemas:
 Construir el montículo inicial.
 Como restablecer los montículos intermedios.
Para restablecer el montículo hay dos posibilidades:
if der ≤ heap-size[A] and A[der] > A[pos-max] then
pos-max = der
if pos-max ≠ i then
intercambiar (A[i], A[pos-max])
Heapify(A, pos-max)

5.1.2 CONSTRUCCIÓN DE UN MONTÍCULO.


Utilizando Heapify:
Build-Heap (A)
Heap-size [A] = length [A]
for I = [length [A]/2] down to 1
do Heapify (A, I)
Algoritmo HeapSort:
HeapSort (A)
1 Build-Heap (A)
2 For j = length [A]down to 2
3 do intercambio (A[1], A[j])
4 heap-size [A] = heap-size [A] − 1
5 Heapify (A, 1)

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 65 de 91
El significado de heapen ciencia computacional es el de una cola de
prioridades (priority queue).
Tiene las siguientes características:
 Un heap es un arreglo de “n” posiciones ocupado por los elementos
de la cola. (Nota: seutiliza un arreglo que inicia en la posición 1 y
no en cero, de tal manera que al implementarla en C# se tienen n+1
posiciones en el arreglo.)
 Se mapea un árbol binario de tal manera en el arreglo que el nodo
en la posición i es el padre de los nodos en las posiciones (2*i) y
(2*i+1).
 El valor en un nodo es mayor o igual a los valores de sus hijos.
Por consiguiente, el nodo padre tiene el mayor valor de todo su
subárbol.

La clase anterior se utiliza para crear el arreglo, invocar un nueva


instancia de la claseHeapSorter y mostrar el contenido del arreglo en
cada iteración durante el proceso deordenamiento.
Deberá crearse una clase de nombre HeapSorter, dentro de la cual se
definirán las funcionesdel método HeapSort y siftDown (para constituir
el montículo).
Luego, del código anteriormente digitado, a continuación escribir el
siguiente código:

public class HeapSorter


{
private int[] a = new int[100]; // arreglo de enteros
private int x = 0; // numero de elementos en el arreglo
public HeapSorter(ref int[] array)
{
a = array;
x = array.Length;
}
// Algoritmo HeapSort
public void sortArray()
{
int i;
int temp;
for (i = (x / 2) - 1; i >= 0; i--)
{
siftDown(i, x);
}
MainClass.showArray(a);
for (i = x - 1; i >= 1; i--)
{
temp = a[0];
a[0] = a[i];
a[i] = temp;
siftDown(0, i - 1);
MainClass.showArray(a);
}
}
public void siftDown(int root, int bottom)
{
bool done = false;
int maxChild;
int temp;
La estructura heap es frecuentemente usada para implementar colas de
prioridad. En este tipo de colas, el elemento a ser eliminado
(borrados) es aquél que tiene mayor (o menor) prioridad. En cualquier

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 66 de 91
momento, un elemento con una prioridad arbitraria puede ser insertado
en la cola. Una estructura de datos que soporta estas dos operaciones
es la cola de prioridad máxima (mínima).
Existen tres categorías de un heap: max heap, min heap y min-max heap.
Un max (min) tree es un árbol en el cual el valor de la llave de cada
nodo no es menor (mayor) que la de los valores de las llaves de sus
hijos (si tiene alguno). Un max heap es un árbol binario completo que
es también un max tree. Por otra parte, un min heap es un árbol binario
completo que es también un min tree.
De la definición se sabe que la llave del root de un min tree es la
menor llave del árbol, mientras que la del root de un max tree es la
mayor.
Si la llave (key) de cada nodo es mayor que o igual a las llaves de sus
hijos, entonces la estructura heap es llamada max heap.
Si la llave (key) de cada nodo es menor que o igual a las llaves desus
hijos, entonces la estructura heap es llamada min heap.
En una estructura min-max heap, un nivel satisface la propiedad min heap,
y el siguiente nivel inferior satisface la propiedad max heap,
alternadamente. Un min-max heap es útil para colas de prioridad de doble
fin.
Las operaciones básicas de un heap son:

 Creación de un heap vacío


 Inserción de un nuevo elemento en la estructura heap.
 Eliminación del elemento más grande del heap.

Su único requisito es que sólo es posible acceder a ellos a través de un


puntero.
Ventajas:
 Soporta las operaciones insertar y suprimir en tiempo O(log N) en
el caso peor.
 Soporta insertar en tiempo constante en promedio y primero en
tiempo constante en el peor caso.
Un heap tiene las siguientes tres propiedades:
 Es completo, esto es, las hojas de un árbol están en a lo máximo
dos niveles adyacentes, y las hojas en el último nivel están en la
posición leftmost.
 Cada nivel en un heap es llenado en orden de izquierda a derecha.
 Está parcialmente ordenado, esto es, un valor asignado, llamado key
del elemento almacenado en cada nodo (llamado parent), es menor que
(mayor que) o igual a las llaves almacenadas en los hijos de los
nodos izquierdo y derecho.

Ejercicios __.__.20__:

1. define las propiedades de un montículo.


2. ¿Cuándo un hijo cumple las propiedades respecto a la raiz?
3. Crea un árbol de 4 nodos binarios.
4. ¿Qué es la estructura de datos Montículo?
5. ¿Cómo se debe ser el valor del nodo padre?

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 67 de 91
PARTE II

DISEÑO DE ALGORITMOS

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 68 de 91
TEMA 6: RECURSIVIDAD Y DISEÑO ITERATIVO
6.1 RECURSIVIDAD
Recursiva: que puede repetirse indefinidamente. Un programa informático.

En su forma más simple recursividad se define como la habilidad de una


función de llamarse a si mismo. Es un concepto bastante fácil de aprender
pero difícil de aplicar.
6.1.1 CARACTERÍSTICAS.
 Ser una herramienta muy potente en determinadas aplicaciones,sobre
todo de cálculo, pudiendo ser utilizada como alternativa a la
repetición o estructura repetitiva.
 Toda función/método recursivo debe tener dos partes:
1. una parte de terminación en la que se deja de hacer llamadas (es
lo que denominaremos caso base) y que constituye la condición de
terminación de la función,permitiendo así que no se produzca una
recursión infinita.
2. y una llamada recursiva con sus propios parámetros, en los que
éstos sufrirán alguna variación.
Cuando una función se llama así misma, se denomina función recursiva y
denominaremos a la llamada a sí misma llamada recursiva.
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.

6.1.2 RECURSIVIDAD EN Java


La recursividad es una técnica potente de programación que puede
utilizarse en lugar de la iteración para resolver determinados tipos de
problemas.
Por ejemplo, para escribir un método que calcule el factorial de un
número entero no negativo, podemos hacerlo a partir de la definición de
factorial:
Si n = 0 entonces
0! = 1
si n>0 entonces
n! = n * (n–1) * (n–2) * ... * 3 * 2 * 1

Esto dará lugar a una solución iterativa en Java mediante un bucle for:
// Método Java no recursivo para calcular el factorial de un número
public double factorial(int n){
double fact=1;
int i;
if (n==0)
fact=1;
else
for(i=1;i<=n;i++)
fact=fact*i;
return fact;
}
Pero existe otra definición de factorial en función de sí misma:
0! = 1
n!=n(n – 1)!, si n>0(El factorial de n es n por el factorial de n-1)
Esta definición da lugar a una solución recursiva del factorial en Java:
// Método Java recursivo para calcular el factorial de un número

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 69 de 91
public double factorial(int n){
if (n==0)
return 1;
else
return n*(factorial(n-1));
}
Un método es recursivo cuando entre sus instrucciones se encuentra una
llamada a sí mismo.
La solución iterativa es fácil de entender. Utiliza una variable para
acumular los productos y obtener la solución. En la solución recursiva se
realizan llamadas al propio método con valores de n cada vez más pequeños
para resolver el problema.
Cada vez que se produce una nueva llamada al método se crean en memoria
de nuevo las variables y comienza la ejecución del nuevo método.
Para entender el funcionamiento de la recursividad, podemos pensar que
cada llamada supone hacerlo a un método diferente, copia del original,
que se ejecuta y devuelve el resultado a quien lo llamó.
En la figura siguiente podemos ver cómo sería la ejecución del programa
Java anterior para calcular el factorial de 3.

Un método recursivo debe contener:


 Uno o más casos base: casos para los que existe una solución directa.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 70 de 91
 Una o más llamadas recursivas: casos en los que se llama sí mismo.
Caso base: Siempre ha de existir uno o más casos en los que los valores
de los parámetros de entrada permitan al método devolver un resultado
directo. Estos casos también se conocen como solución trivial del
problema.
En el ejemplo del factorial el caso base es la condición:
if (n==0)
return 1;
si n=0 el resultado directo es 1 No se produce llamada recursiva

Llamada recursiva: Si los valores de los parámetros de entrada no cumplen


la condición del caso base se llama recursivamente al método. En las
llamadas recursivas el valor del parámetro en la llamada se ha de
modificar de forma que se aproxime cada vez más hasta alcanzar al valor
del caso base.
En el ejemplo del factorial en cada llamada recursiva se utiliza n-1
return n * ( factorial(n-1) );
por lo que en cada llamada el valor de n se acerca más a 0 que es el caso
base.
La recursividad es especialmente apropiada cuando el problema a resolver
(por ejemplo cálculo del factorial de un número) o la estructura de datos
a procesar (por ejemplo los árboles) tienen una clara definición
recursiva.
No se debe utilizar la recursión cuando la iteración ofrece una solución
obvia. Cuando el problema se pueda definir mejor de una forma recursiva
que iterativa lo resolveremos utilizando recursividad.
Para medir la eficacia de un algoritmo recursivo se tienen en cuenta tres
factores:
 Tiempo de ejecución
 Uso de memoria

6.1.3 LEGIBILIDAD Y FACILIDAD DE COMPRENSIÓN.


Las soluciones recursivas suelen ser más lentas que las iterativas por el
tiempo empleado en la gestión de las sucesivas llamadas a los métodos.
Además consumen más memoria ya que se deben guardar los contextos de
ejecución de cada método que se llama.
A pesar de estos inconvenientes, en ciertos problemas, la recursividad
conduce a soluciones que son mucho más fáciles de leer y comprender que
su correspondiente solución iterativa. En estos casos una mayor claridad
del algoritmo puede compensar el coste en tiempo y en ocupación de
memoria.
De todas maneras, numerosos problemas son difíciles de resolver con
soluciones iterativas, y sólo la solución recursiva conduce a la
resolución del problema (por ejemplo, Torres de Hanoi o recorrido de
Árboles).

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 71 de 91
Veamos el algoritmo en pseudo código
Hanoi( int n, int start, int destination, int open )
Hanoi( n - 1, start, open, destination )
Move( n, start, destination )
Hanoi( n - 1, open, destination, start )

6.2 DISEÑO ITERATIVO.


La recursión y la iteración aceleran la ejecución de un programa.
6.2.1 RECURSIÓN
Una operación recursiva es un proceso que se repite hasta que se llega a
una instrucción final desde dentro de la operación. La técnica recursiva
más habitual en la programación de computadoras es un método de reducción
de un problema, desde arriba hacia abajo, consiguiendo una versión del
propio problema cada vez más simple hasta que se llega a un caso base. La
solución al caso base se combina con la solución de cada uno de los
problemas anteriores hasta llegar al primero, al caso más complicado.
6.2.2 ITERACIÓN
En programación de computadoras, una operación iterativa es la que repite
un proceso durante un número determinado de veces (iteraciones),
dependiendo de los parámetros definidos por el programador. Normalmente
la salida de una iteración del proceso se utiliza como punto de inicio
para la siguiente iteración. Cada paso origina el paso siguiente. El
proceso continúa hasta que se alcanza una meta determinada y el proceso
termina.
6.2.2.1 DIFERENCIA PRINCIPAL
La diferencia más importante entre las operaciones recursivas y las
iterativas es que los pasos de una operación iterativa se realizan uno
cada vez y dirigen la ejecución directamente al siguiente paso. En una
operación recursiva, cada paso después del paso inicial es una réplica
del paso anterior. Además, desde arriba hacia abajo, cada paso es un poco
más sencillo que el que hay justo "encima". Al final de la operación,
todas las soluciones se combinan para resolver el problema.

Ejemplos:
Un ejemplo habitual de una operación cursiva es un factorial. El factoral
de un número es el producto de los enteros positivos menores o iguales
que ese número. Resolver este problema recursivamente requiere
multiplicar el número inicial por sí mismo menos 1. La expresión
recursiva es n(n - 1), donde n es el número inicial. Cada paso es un poco
más sencillo que el paso anterior. La operación termina cuando n se
reduce a 1. Un ejemplo de una iteración es encontrar la suma de un
conjunto de números. La expresión iterativa es (n + (n + 1)), donde n es
el número inicial. Cada paso empieza con la solución del paso anterior.
La operación termina cuando n llega al número deseado.

6.2.2.2 VENTAJAS Y DESVENTAJAS:

La iteración construye la solución por agregación correlativa de las


partes desde la primera hasta la última, mientras que la recursión parte
del todo y resuelve el problema desagregándolo en partes más pequeñas que
finalmente necesita juntar para reconstruir la solución. La diferencia
escriba pues en el planteamiento y en el enfoque de partida, aunque la
mecánica de resolución es idéntica en ambos métodos.
Existe, por tanto, una dualidad entre recursión e iteración, y así, un
programa recursivo puede transformarse en uno iterativo, y viceversa.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 72 de 91
 Ventajas:
- Ventajas de la Recursión ya conocidas
– Soluciones simples, claras.
– Soluciones elegantes.
– Soluciones a problemas complejos.

• Desventajas: INEFICIENCIA
– Sobrecarga asociada con las llamadas a subalgoritmos.

• Una simple llamada puede generar un gran número de llamadas


recursivas. (Fact(n) genera n llamadas recursivas)

• ¿La claridad compensa la sobrecarga?.

• El valor de la recursividad reside en el hecho de que se puede usar


para resolver problemas sin fácil solución iterativa.

• La ineficiencia inherente de algunos algoritmos recursivos:


A veces, podemos encontrar una solución iterativa simple,
que haga que el algoritmo sea más eficiente.

6.2.2.3 SEMEJANZAS Y DIFERENCIAS:

Semejanzas:
- Ambas estrategias implican repetición: la iteración usa explícitamente
una estructura de repetición mientras que en la recursión está implícita.
- Ambas requieren una condición de corte: la iteración cuando llega al
tope del contador y la recursión cuando alcanza el estado base.
- Ambas se aproximan a la solución gradualmente: la iteración modificando
las variables iterativamente y la recursión produciendo versiones más
sencillas del problema original.
- Ambas pueden caer en bucles infinitos.

Diferencias:
- La recursividad produce algoritmos cortos y elegantes, mientras que la
iteración presentan la habitual forma de uno o varios bucles.
- La recursión conlleva una repetida invocación de la función que, en
general, incurre en un gasto de tiempo y de memoria que no se da en la
versión iterativa.

Proponer Ejercicios para casa.

TEMA 7.ALGORITMOS VORACES Y ALGORITMOS DIVIDE Y VENCERÁS


7.1 ALGORITMOS VORACES. INTRODUCCIÓN. ESQUEMA VORAZ.
En ciencias de la computacion, un algritmo voraz(tambien conocido como
ávido, devorador o greedy) es una estrategia de busqueda por la cual se
sigue una heuristica consistente en elegir la opcion optima en cada
paso local con la esperanza de llegar a una solucion general optima.
Se aplica normalmente a problemas de optimización:
Búsqueda del valor óptimo (máximo o mínimo) de una cierta función
Objetivo. En un dominio determinado (restricciones del problema)
Ejemplo: problema de la mochila con fraccionamiento, cambio demonedas.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 73 de 91
 La solución se puede presentar como una secuencia dedecisiones
 Medida local de optimización.
 Las decisiones son irreversibles.
 No siempre encuentra la solución óptima, pero en ocasionespermite
encontrar una solución aproximada con un costecomputacional bajo.

Ejemplo de un cajero automático:


Suministrar la cantidad de billetes solicitada de forma que el número
total de billetes sea mínimo.
M=11000 fcos, B={1000, 2000, 5000}
11x1000 (11 billetes)
5x2000 + 1x1000 (6 billetes)
2x5000 + 1x1000 (3 billetes)
La estrategia voraz consiste en coger el billete más grande:
A veces no hay solución; p.e. Si M=3000 y no hay billetes de 1000
A veces no la encuentra; p.e. M=11000 y no hay billetes de 1000
A veces encuentra una factible pero no óptima; p.e. B={100, 500, 1100,
5000} yM=6500. La solución que se obtendría sería: 1x5000+1x100+4x100
(6 billetes)

Elementos de un algoritmo voraz:

C Conjunto de elementos o candidatos


S Conjunto de elementos de la solución en curso
Solucion(S) ¿Es S solución?
Factible(S) ¿Es S completable o factible?
Selección(C) Seleccionar un candidato
f Función objetivo

Esquema Voraz
Algoritmo Voraz: Obtiene un subconjunto de C queoptimiza la función
objetivo. Para ello:
 Seleccionar en cada instante un candidato (criterio local de
 Optimización).
 Añadir el candidato a la solución en curso si es completable (o
 factible), sino rechazarlo.
 Repetir los dos puntos anteriores hasta obtener la solución (o
noquedan más candidatos).
Recordemos ...
Un algoritmo voraz no siempre proporciona la solución óptima.
Las decisiones son irreversibles.
Ejemplo de cambio de monedas:

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 74 de 91
Un algoritmo voraz determina el minimo numero de monedas que debe
devolverse en el cambio. En la figura se muestran los pasos que un ser
humano deberia seguir para emular a un algoritmo voraz para acumular 36
centimos usando solo monedas de valores nominales de 1, 5, 10, 20. La
moneda del mayor valor menor que el resto debido es el optimo local en
cada paso. Nótese que en general el problema de devolucion del cambio
requiereprogramacion dinamica o lineal para encontrar una solucion
optima. Sin embargo, en muchos sistemas monetarios, incluyendo el euro
y el dólaramericano, son casos especiales donde en la estrategia del
algoritmo voraz da con la solucion optima.
Problema del cambio en monedas.
– Se trata de devolver una cantidad de pesetas con el menor número
posible de monedas.
– Se parte de:
 Un conjunto de tipos de monedas válidas, de las que se supone que
hay cantidad suficiente para realizar el desglose.
 Y de un importe a devolver.

Elementos fundamentales del esquema:


– Conjunto de candidatos: cada una de lasmonedas de los diferentes
tipos que se puedenusar para realizar el desglose del importe dado.
– Solución: un conjunto de monedas devuelto trasel desglose y cuyo
valor total es igual al importea desglosar.
– Completable: la suma de los valores de lasmonedas escogidas en un
momento dado nosupera el importe a desglosar.
– Función de selección: elegir si es posible lamoneda de mayor valor de
entre las candidatas.
– Función objetivo: número total de monedasutilizadas en la solución
(debe minimizarse).

Solución:
-----------------------------------------------------
tipo moneda=(M25,M10,M5,M1) {por ejemplo}
función cambia(importe:nat;
valor:vector[moneda] de nat)
devuelve vector[moneda] de nat
variable mon:moneda;
cambio:vector[moneda] de nat
inicio
para todo mon en moneda hacercambio[mon]:=0
fpara;
para mon:=M25 hasta M1 hacer
mq valor[mon]£importe hacer
cambio[mon]:=cambio[mon]+1;
importe:=importe-valor[mon]
fmq
fpara;
devuelve cambio
fin
-------------------------------------------------------

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 75 de 91
7.2 ALGORITMOS DIVIDE Y VENCERÁS.
Este tipo de algoritmos se usa en la resolución de problemas que pueden
ser simplificados en problemas más sencillos, cuando hablo de
simplificación del problema me refiero a reducir el volumen de datos
manejados.
Se pueden distinguir tres partes fundamentales:

1. Los casos base: son las sentencias que se ejecutarán cuando no sea
posible reducir más el problema
2. La simplificación del problema: es un bloque de instrucciones cuya
finalidad es dividir el problema en subproblemas menores del mismo
tipo.
3. La parte recursiva: consiste en hacer una llamada a la propia
función para que trabaje sobre los subproblemas obtenidos
anteriormente.

Ejemplo 1:
Se quiere encontrar un algoritmo para obtener, si lo hay, el elemento
mayoritario de una colección de números enteros.
Se considera elemento mayoritario a aquel que aparece más de n/2 veces,
donde n es la cantidad de elementos de la colección de datos.
En la imagen se muestra el diagrama de flujo del algoritmo implementado
para la resolución del problema.

A continuación se muestra el código en Java correspondiente a la implementación del algoritmo.

public class Mayoritario {


/**
* Excepción personalizada que se lanza cuando no hay elemento mayoritario
**/
public static class NoMayoritarioException extends Exception{
public NoMayoritarioException(){
super("No hay elementos mayoritarios");

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 76 de 91
}
}
/**
* Devuelve un entero correspondiente al elemento mayoritario de un
*ArrayList de enteros.
*¡Ojo porque el contenido del ArrayList se pierde!
* @param cjto Si quiere conservar los datos pase una copia del original
* @return
* @throws mayoritario.Mayoritario.NoMayoritarioException
*/
public int calcMayoritario(ArrayList<Integer> cjto)throws NoMayoritarioException{
//Inicio de los casos base
if(cjto.size()<2){
throw new NoMayoritarioException();
}
switch(cjto.size()){
case 2:
if(cjto.get(0) ==cjto.get(1)) {
return cjto.get(0);
}
throw new NoMayoritarioException();
case 3:
if(cjto.get(0) ==cjto.get(1)||cjto.get(0) ==cjto.get(2)) {
return cjto.get(0);
}else if(cjto.get(2) ==cjto.get(1)){
return cjto.get(1);
}
throw new NoMayoritarioException();
}
if(cjto.size()==2){
}
//Selección de candidatos
ArrayList<Integer> candidatos;
candidatos = new ArrayList<>();
for(int i=0;i<cjto.size()-2;i+=3){
if(cjto.get(i) ==cjto.get(i+1) || cjto.get(i) ==cjto.get(i+2)){
candidatos.add(cjto.get(i));
}if(cjto.get(i+1) ==cjto.get(i+2)){
candidatos.add(cjto.get(i+1));
}
}
int len=cjto.size();
if(len%2==0 && cjto.get(len-2) ==cjto.get(len-1)){
candidatos.add(cjto.get(len-1));
}
//Aplico la recursión
if(candidatos.size()>=cjto.size()/2){
//Alivio un poco la carga espacial, con lo que se pierden los datos de partida
cjto=null;

return this.calcMayoritario(candidatos);
}else{
throw new NoMayoritarioException();
}
}
}

Comparación con otras alternativas


Al aplicar este algoritmo en cada llamada a la función se recorre una
sola vez la colección de datos y de una a otra llamada el número de
elementos es como mucho la mitad del anterior.
Las alternativas más directas para resolver este problema sin usar un
algoritmo de este tipo pasarían por recorrer todos los elementos hasta
encontrar uno que se repita más de n/2 veces. El caso más favorable es

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 77 de 91
cuando el primer elemento es el mayoritario por lo que tendrá
complejidad lineal, pero en el resto de casos será cuadrática
Otra opción pasa por ordenar primero la colección, pero habría que
tener en cuenta la complejidad del algoritmo de ordenación.
Otra posibilidad sería utilizar un algoritmo dinámico que se apoyase en
una colección de posibles soluciones, donde cada elemento está
compuesto por el valor y el número de repeticiones. En ese caso se
recorrería una sola vez la colección inicial y un máximo de n veces, en
el peor de los casos, la colección auxiliar para comprobar si el valor
i-ésimo ya se encuentra en la colección auxiliar e insertarlo o
incrementar el valor de sus repeticiones. Por lo que tanto la
complejidad espacial como temporal serán mayores.

Ejemplo 2: algoritmo quickSort


El algoritmo quickSort es uno de los casos más típicos de aplicación de
algoritmos del tipo divide y vencerás. Consiste en la ordenación de una
colección indexada de objetos dividiendo de forma recursiva la olección
en subconjuntos que se caracterizan porque los elementos del ubconjunto
anterior aun elemento dado, pivote, son menores que este y los del
ubconjunto posterior son mayores. Al hacer subconjuntos de los
subconjuntos de forma recursiva se logra dividir el problema en muchos
subproblemas triviales.
Cabe destacar que con este algoritmo no es necesario un paso posterior
de mezcla para obtener la colección ordenada.
La complejidad del algoritmo es cuadrática en el peor de los casos y
nLogn en los casos mejor y medio. El caso será mejor o peor en función
de la distancia entre el pivote elegido y la mediana del conjunto de
datos, cuanto más alejado peor. En la implementación que se muestra a
continuación se escoge como pivote al primer elemento del subconjunto,
por lo que este algoritmo es susceptible de ser optimizado sin mucho
esfuerzo, aunque hay que tener en cuenta la complejidad del algoritmo
de elección del pivote para estimar la eficiencia final del nuevo
algoritmo.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 78 de 91
A continuación se muestra el código en Java correspondiente a la implementación del algoritmo.
public static void quickSort(int[] a,int prim, int ult){
if(prim<ult){
//Genero subconjuntos
int l=pivote(a,prim,ult);
//Aplico recursión sobre los subconjuntos
if(l>prim){quickSort(a,prim,l-1);}
if(l<ult){quickSort(a,l+1,ult);}
}//Caso base prim=ult
}
/**
* Devuelve la posición del pivote, elemento que por la izquierda
solo
* tiene elementos de valor inferior y por la derecha de valor
superior.
* Sobra decir que lo que hace es colocar los elementos a derecha
o
* del pivote según su valor.
*/
private static int pivote(int[] a,int prim, int ult){
int i=prim+1;
int l=ult;
while(a[i]<=a[prim] && i<ult){i++;}
while(a[l]>a[prim]){l--;}
while(i<l){
intercambia(a,i,l);
while(a[i]<=a[prim]){i++;}
while(a[l]>a[prim]){l--;}
}
intercambia(a,prim,l);
return l;
}

Tanto el ordenamiento por mezcla como el ordenamiento rápido emplean un


paradigma algorítmico común que se basa en la recursividad. Este
paradigma, divide y vencerás, separa un problema en subproblemas que se
parecen al problema original, de manera recursiva resuelve los
subproblemas y, por último, combina las soluciones de los subproblemas
para resolver el problema original. Como divide y vencerás resuelve
subproblemas de manera recursiva, cada subproblema debe ser más pequeño
que el problema original, y debe haber un caso base para los
subproblemas. Debes pensar que los algoritmos de divide y vencerás
tienen tres partes:
1. Divide el problema en un número de subproblemas que son instancias
más pequeñas del mismo problema.
2. Vence los subproblemas al resolverlos de manera recursiva. Si son los
suficientemente pequeños, resuelve los subproblemas como casos base.
3. Combina las soluciones de los subproblemas en la solución para el
problema original.
Puedes recordar fácilmente los pasos para un algoritmo de divide y
vencerás como divide, conquista, combina. Aquí está cómo ver un paso,
al suponer que cada paso de dividir crea dos subproblemas (aunque
algunos algoritmos de divide y vencerás crean más de dos):

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 79 de 91
Si expandimos a dos pasos recursivos más, se ve así:

Como divide y vencerás crea por lo menos dos subproblemas, un algoritmo


de divide y vencerás hace muchas llamadas recursivas.

Ejemplo del codigo fuente divide y venceras en java:


comparar si los dos son iguales o no con el siguiente código:

public static boolean sonIgualesDyV(int[] a, int[] b,


int ini, int fin)
{
if(fin - ini <= 1)
{
if((a[ini] != b[ini]) || (a[fin] !=b[fin]))
{
return false;
} else
{
return true;
}
} else
{
int medio = (ini + fin)/2;
boolean resultadoAux = sonIgualesDyV(a,b,ini, medio);
boolean resultadoAux2 = sonIgualesDyV(a,b, medio+1, fin);
return (resultadoAux && resultadoAux2);
}
Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 80 de 91
}

// metodo ejecutor:
public static void main(String[] args) {
// TODO code application logic here

int a[]={1,2,7,8,9,10};
int b[]={1,2,7,8,9,10};

if (sonIgualesDyV(a,b,0, a.length-1))
System.out.println("Los vectores son iguales");
else
System.out.println("Los vectores no son iguales");
}

Ejemplo del codigo fuente divide y venceras en C++:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){


signed int serie[1500];
int smallstart,smallend;
int currstart,currend;
int currlong,smalllong;
int i,num;
currlong=0;
smalllong=0;
currstart=0;
smallstart=0;

scanf("%d",&num);
for (i=0;i<num;i++){
scanf("%d",&serie[i]);
}

for (i=0;i<num;i++){
if (i>0 && serie[i]>serie[i-1] && currlong){
currlong++;
currend=i;
if (currlong>smalllong){
smallstart=currstart;
smallend=currend;
smalllong=currlong;
}
}else
if (currlong==0){
if (serie[i]<0){
currlong=1;
currstart=i;
currend=i;
}
}else if (serie[i]<serie[i-1] && currlong){
if (serie[i]<0){
currlong=1;
currstart=i;
currend=i;
}
}

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 81 de 91
}
for (i=smallstart;i<=smallend;i++){
printf("%i ",serie[i]);
}
return 0;
}

Ejercicios para casa:


1.
2.
3.
4.
5.
6.
7.

TEMA 8: ALGORITMOS DE EXPLORACIÓN DE GRAFOS


8.1 GRAFOS
Es una estructura de datos que consiste en un conjunto de nodos y un
conjunto de arcos que establecen relaciones entre los nodos. Cada arco
en un grafo se especifica por medio de un par de nodos.

8.2 EXPLORACIÓN DE GRAFOS


A la hora de explorar un grafo, nos encontramos con dos métodos
distintos. Ambos conducen al mismo destino (la exploración de todos los
vértices o hasta que se encuentra uno determinado), si bien el orden en
que éstos son "visitados" decide radicalmente el tiempo de ejecución de
un algoritmo, como se verá posteriormente.
En primer lugar, una forma sencilla de recorrer los vértices es
mediante una función recursiva, lo que se denomina búsqueda en
profundidad. La sustitución de la recursión (cuya base es la estructura
de datos pila) por una cola nos proporciona el segundo método de
búsqueda o recorrido, la búsqueda en amplitud o anchura.

8.3 TIPOS DE GRAFOS


8.3.1 GRAFOS BI-DIRECCIONALES
El mas simple de los grafos en donde cada arco apunta
exactamente a dos nodos.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 82 de 91
8.3.2 GRAFOS UNIDIRECCIONAL
Cada arco apunta a un solo nodo.

Cada nodo puede apuntar a otro nodo a través de otro arco.

8.3.3 GRAFOS DE PESO


En este grafo introducimos un nuevo nivel de complejidad al
introducir un peso al arco que conecta los nodos.
Imaginémonos los vuelos entre aeropuertos de cierta línea aérea.

Ejercicios para casa:


1.
2.
3.
4.
5.
6.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 83 de 91
PARTE III

INTELIGENCIA ARTIFICIAL

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 84 de 91
TEMA 9: TÉCNICAS DE BÚSQUEDA HEURÍSTICA EN GRAFOS.
Los algoritmos de búsqueda en grafos nacen por la necesidad de crear
un mecanismo de navegación autónoma, bien sea de robots, coches, o
personajes en un videojuego. Algunos de los más conocidos
son A*, LPA*, o D*.
Los métodos de búsqueda heurística disponen de alguna información sobre
la proximidad de cada estado a un estado objetivo, lo que permite
explorar en primer lugar los caminos más prometedores.
Son características de los métodos heurísticos:
No garantizan que se encuentre una solución, aunque existansoluciones.
_ Si encuentran una solución, no se asegura que ésta tenga lasmejoresas
propiedades (que sea de longitud mínima o de costeóptimo).
_ En algunas ocasiones (que, en general, no se podrán determinar
apriori), encontrarán una solución (aceptablemente buena) en untiempo
razonable .
En general, los métodos heurísticos son preferibles a losmétodos no
informados en la solución de problemasdifíciles para los que una
búsqueda exhaustiva necesitaríaun tiempo demasiado grande. Esto cubre
prácticamente latotalidad de los problemas reales que interesan en
Inteligencia Artificial.
_ La información del problema concreto que estamosintentando resolver
se suele expresar por medio deheurísticas.
_ El concepto de heurística es difícil de aprehender. Newell,Shaw y
Simon en 1963 dieron la siguiente definición: "Unproceso que puede
resolver un problema dado, pero queno ofrece ninguna garantía de que lo
hará, se llama unaheurística para ese problema".

Si nos planteamos seguir concretando como aprovechar lainformación


sobre el problema en sistemas de producción,la siguiente idea consiste
en concentrar toda la informaciónheurística en una única función que se
denomina funciónde evaluación heurística. Se trata de una función
queasocia a cada estado del espacio de estados una ciertacantidad
numérica que evalúa de algún modo loprometedor que es ese estado para
acceder a un estadoobjetivo. Habitualmente, se denota esa función por
h(e).
9.1 EJEMPLOS DE FUNCIONES HEURÍSTICAS:

Veamos ejemplos de heurísticas para algunos problemas concretos. Para


el problema del 8-puzzle tenemos la siguientes heurísticas:
a) La basada en la distancia Manhattan (o distancia taxi). Se asocia a
cada casilla un número que es la suma de las distancias horizontal y
vertical a su posición en el tablero objetivo (esto es, la suma de
diferencias de sus coordenadas x e y). La función heurística es la suma
de las distancias de cada una de las casillas (excluyendo la que se
encuentra vacía).
2 3
1 8 4
7 6 5
Fig. 1

H(Ei)=2 (1 de la casilla 1 más 1 de la casilla 8)

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 85 de 91
b) Otra heurística, mucho más simple, consiste en contar el número de
casillas que están fuera de su sitio (respecto al tablero objetivo). Es
una heurística más pobre que la anterior, puesto que no usa la
información relativa al esfuerzo (número de movimientos) necesario para
llevar una pieza a su lugar.

El problema del viajante.

_ Estado inicial: un viajante se encuentra en una capital de provincia.


_ Estado meta: quiere viajar a otra capital por la mejor ruta posible
(lamás corta)
_ Medios: Las capitales de provincia colindantes están unidas por
carreteras; se dispone de un mapa con la disposición de lasprovincias y
sus "coordenadas" en kilómetros respecto al "centro"(por ejemplo,
Madrid, con coordenadas (0,0)).
_ Una función heurística para ese problema consiste en asignar a cada
estado un valor que es la distancia aérea (en línea recta) con el
estadoobjetivo. Dicha distancia es la distancia euclídea entre las
coordenadasde dos ciudades.
_ Se elige una ciudad como siguiente en el camino cuando la suma de la
distancia a la ciudad actual más la distancia aérea a la meta sea la
menor.
Una persona quiere viajar de Madrid a Santanderconsiderando el mapa de
carreteras de la fig. 2.

fig. 2.

Cuatro alternativas en la primera etapa: Cáceres,Palencia, Zaragoza y


Valencia.
_ Función heurística considerando la distancia aérea:

Distancia (Madrid, Santander)=Distancia(Madrid, Palencia)+Distancia


aérea (Palencia, Santander)que es más pequeña que la obtenida a través
de CáceresDistancia (Madrid,
Santander)=Distancia(Madrid,Cáceres)+Distanciaaérea (Cáceres,
Santander)
_ Función heurística considerando distancias parciales:

Distancia (Madrid, Santander)=Distancia(Madrid, Palencia)+Distancia


(Palencia, Santander)

9.2 MÉTODOS DE ESCALADA


Se llaman de escalada (o de ascensión a la colina) porque tratan de
elegir en cada paso un estado cuyo valor heurístico sea mayor que el
del estado activo en ese momento.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 86 de 91
Se dividen en dos grupos:
_ Los métodos irrevocables que no preven la vuelta a un lugar del
espaciode estados si el camino resulta inadecuado.
_ Los métodos tentativos en los que sí podemos volver hacia atrás si
prevemos que el camino elegido no es el más adecuado.

9.2.1 MÉTODOS DE ESCALADA IRREVOCABLES.


_ La lista ABIERTA no puede poseer más de un elemento, esdecir, sólo se
mantiene en expectativa un único camino.
_ El modo en que se determina qué estado es el que sigue a unodado, da
lugar a dos variantes de los esquemas en escaladairrevocables:

9.2.1.1 LA ESCALADA SIMPLE. En este método, en el momento es que


seencuentra un estado E que es más favorable que el que se
estáexpandiendo, dicho estado E es devuelto sin generar el resto
deestados hijos.

9.2.1.2 LA ESCALADA POR LA MÁXIMA PENDIENTE. En este caso, se


generantodos los hijos de un estado, se calculan sus valores
heurísticos yse determina uno de los estados de mejor valor heurístico;
secompara dicho valor con el de el estado expandido y si es mejor,se
devuelve ese estado como expansión.
En ambos casos, si ningún estado hijo es mejor que el estado que está
siendo expandido, no se devuelve nada, lo que conllevará que la
búsqueda termine sin haber encontrado un objetivo. Nótese que la
escalada simple es mucho más dependiente que la escalada por la máxima
pendiente del orden de disparo de las reglas (pese a que ésta última
también lo es: el valor heurístico mejor puede ser alcanzado en varios
hijos y, en ese caso, el método no dice nada sobre qué estado elegir).

9.2.2 ALGORITMO DE ESCALADA SIMPLE


El algoritmo:
1. Denominar m al estado inicial y evaluarlo.
Si es estado objetivo,entonces devolverlo y terminar, si no,
convertirlo en estado actual.
Asignar m a una varibla llamada elegido.
2. Repetir hasta que se encuentre solución o hasta que una iteración
completa no produzca un cambio en el estado actual:
2.1 Expandir m. Para ello, 1) Aplicar cualquier operador aplicable al
estado actual m y obtener un nuevo estado Ei.
2.2 Evaluar Ei:
2.2.1 Si Ei es estado objetivo, devolverlo y terminar.
2.2.2 Si Ei no es estado objetivo no es así, evaluar si Ei es mejor que
el estado actual: (H(Ei) ->H(elegido)), en cuyo caso hacer m=Ei.
2.2.3 Si f(elegido) # f(m) asignar m=elegido, y volver a 2.

EJERCICIOS PARA CASA:

1.
2.
3.
4.
5.

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 87 de 91
TEMA 10. INTRODUCCIÓN A APRENDIZAJE AUTOMÁTICO

10.1 INTRODUCCIÓN
Vivimos un momento en el que cada vez es más habitual escuchar
hablar sobre el aprendizaje automático pero, ¿sabemos realmente
qué es y para qué nos sirve?
El aprendizaje automático, también conocido como Machine
Learning(ML), es una rama de la inteligencia artificial (AI) que
tiene como objetivo desarrollar técnicas que permitan a las
máquinas aprender por sí solas.

En el mundo se generan a diario 2.5 trillones de bytes de


información. El 90% de los datos a nivel mundial se han creado
en los últimos 2 años y provienen de mil sitios: publicaciones
en redes sociales, imágenes y vídeos digitales, registros de
compra y transacciones y señales de GPS de los móviles…
A este conjunto masivo de datos se le llama Big Data. Es a
partir de esta fuente masiva de datos cuando nace la necesidad
de un profesional que debe conocer y generar un uso para esta
información: el data scientist o científico de datos.
El aprendizaje automático es una nueva y eficaz herramienta que
puede ayudarnos con tareas muy diversas, desde filtrar una
colección de fotos hasta enfrentarnos con algunos de los
principales desafíos globales en materia de salud, medio
ambiente, etc. En estos vídeos examinaremos qué son estas
tecnologías y cómo las empresas pueden aplicarlas en la vida
real para estimular su crecimiento.
El proceso de aprendizaje automático es similar al de la minería
de datos. Ambos sistemas buscan entre los datos para encontrar
patrones. Sin embargo, en lugar de extraer los datos para la
comprensión humana como es el caso de las aplicaciones de
minería de datos,el aprendizaje automático utiliza esos datos
para detectar patrones en los datos y ajustar las acciones del
programa en consecuencia.
10.2 TIPOS DE APRENDIZAJE AUTOMÁTICO
La inteligencia artificial está en su mejor época, pues la gran
mayoría de tecnologías emergentes poseen módulos que les
permiten interactuar de forma más autónoma y humanizada. Dentro
de las múltiples áreas que comprende la inteligencia artificial,

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 88 de 91
se encuentra la de aprendizaje automático, o Machine Learning
(ML) por su traducción en inglés.
Los algoritmos de ML pretenden que las computadoras aprendan a
tomar decisiones sin la necesidad de ser programadas
explícitamente. Es por ello que hoy en día podemos escuchar
acerca de autos de conducción autónoma, agentes virtuales de
atención al cliente (chatbots), sistemas de recomendación y
recolección de datos (Netflix, Google, Facebook).
Para adentrarnos más al mundo de ML, es importante conocer la
clasificación de sus algoritmos. Dependiendo de las necesidades
del problema, el ambiente en el que se van a desenvolver y los
factores que afectarán la toma de decisiones, podemos encontrar
distintos tipos de algoritmos de aprendizaje, entre los cuales
vamos a hablar de 3 de ellos: supervisado, no supervisado y por
refuerzo.

10.2.1 APRENDIZAJE SUPERVISADO(Supervised machine learning)


En los algoritmos de aprendizaje supervisado se genera un modelo
predictivo, basado en datos de entrada y salida. La palabra
clave “supervisado” viene de la idea de tener un conjunto de
datos previamente etiquetado y clasificado, es decir, tener un
conjunto de muestra el cual ya se sabe a qué grupo, valor o
categoría pertenecen los ejemplos. Con este grupo de datos, el
cual llamamos datos de entrenamiento, se realiza el ajuste al
modelo inicial planteado. De esta forma es como el algoritmo va
“aprendiendo” a clasificar las muestras de entrada comparando el
resultado del modelo, y la etiqueta real de la muestra,
realizando las compensaciones respectivas al modelo de acuerdo a
cada error en la estimación del resultado. Por ejemplo, el
aprendizaje supervisado ha sido utilizado para la programación
de vehículos autónomos. Algunos métodos y algoritmos que podemos
implementar son los siguientes:
 K vecinos más próximos (K-nearest neighbors)
 Redes neuronales artificiales (Artificial neural networks)
 Máquinas de vectores de soporte (Support vector machines)
 Clasificador Bayesiano ingenuo (Naïve Bayes classifier)
 Árboles de decisión (Decision trees)
 Regresión logística (Logistic regression)

Figura 1. Diagrama de flujo del aprendizaje automatico

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 89 de 91
10.2.2 APRENDIZAJE NO SUPERVISADO (Unsupervised machine
learning)
Los algoritmos de aprendizaje no supervisado trabajan de forma
muy similar a los supervisados, con la diferencia de que éstos
solo ajustan su modelo predictivo tomando en cuenta los datos de
entrada, sin importar los de salida. Es decir, a diferencia del
supervisado, los datos de entrada no están clasificados ni
etiquetados, y no son necesarias estas características para
entrenar el modelo. Dentro de este tipo de algoritmos, el
agrupamiento o clustering en inglés, es el más utilizado, ya que
particiona los datos en grupos que posean características
similares entre sí. Una aplicación de estos métodos es la
compresión de imágenes. Entre los principales algoritmos de tipo
no supervisado destacan:
 K-medias (K-means)
 Mezcla de Gaussianas (Gaussian mixtures)
 Agrupamiento jerárquico (Hierarchical clustering)
 Mapas auto-organizados (Self-organizing maps)

Figura 2. Diagrama de flujo del aprendizaje automatico

10.2.3 APRENDIZAJE POR REFUERZO(Reinforcement learning)


Los algoritmos de aprendizaje por refuerzo definen modelos y
funciones enfocadas en maximizar una medida de “recompensas”,
basados en “acciones” y al ambiente en el que el agente
inteligente se desempeñará. Este algoritmo es el más apegado a
la psicología conductista de los humanos, ya que es un modelo
acción-recompensa, que busca que el algoritmo se ajuste a la
mejor “recompensa” dada por el ambiente, y sus acciones por
tomar están sujetas a estas recompensas. Este tipo de métodos
pueden usarse para hacer que los robots aprendan a realizar
diferentes tareas. Entre los algoritmos más utilizados podemos
nombrar:
 Programación dinámica (Dynamic programming)
 Q-learning
 SARSA

Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 90 de 91
Figura 3. Diagrama de flujo del aprendizaje automatico

Ningún enfoque es mejor que el otro, cada tipo de aprendizaje


tiene sus ventajas y áreas de oportunidad. El reto está en saber
determinar bien la naturaleza de nuestro problema al cuál
queramos aplicarle algún algoritmo de ML, para así determinar
cuál tipo de aprendizaje automático es el que mejor nos
conviene.

EJERCICIOS DEL TEMA 10_1

1.
2.
3.
4.
5.
6.

----------------------------------FIN DE LA ASIGNATURA-----------------------------------
Finalmente quiero decir que tras tantas horas preparando los temas, podría suceder que se
me haya colado algún gazapo, falta de ortografía, e incluso alguna "burrada", ello a pesar de
que antes de confirmar el capítulo siempre he realizado una revisión para hallar este tipo de
cosas; si a pesar de ello encontráis alguno, lo siento de corazón, no ha sido mi intención.
Si queréis enviarme algún correo dando vuestra opinión sobre el curso (qué os ha parecido, si
muy difícil, si opináis que el desarrollo ha sido adecuado, carencias encontradas, y en fin,
sugerencias), no tenéis más que enviarme un correo mediante las direcciones:
perdupri@gmail.com o pergend@yahoo.com
Teléfono de contacto: 551 296 103
Twitter: @DupriPergentino
facebook: H Pergen Dupri

Muchísimas gracias a todos y un abrazo de vuestro profe:


H. Pergentino Esimi Mbomio Angué alias Pergen
*Ingeniero Técnico de Sistemas Informáticos y de Gestión*
Dirección:Plaza de Ela Nguema (Malabo B-N)
REPUBLICA DE GUINEA ECUATORIAL

Malabo, 30 septiembre de 2019


Profesor Hakim Pergentino Esimi Mbomio Angué Ing. de sistemas Tel: 551 296 103 email: perdupri@gmail.com Facultad de Ciencias Económicas UNGE
Página 91 de 91

Potrebbero piacerti anche