Ensamblador

Para mi primer entrega de Lenguaje Ensamblador decidí hacer un programa sencillo pero que cubriera lo básico del lenguaje;

mi programa realiza la funcion la siguiente formula:  nCr

esta formula calcula la cantidad de combinaciones que se pueden generar con n y r  , realmente se le pueden llamar de la forma que se desee n combinacion con m ,n=numero de variables totales, m=numero de variables que se combinaran.

aqui esta el codigo en C… en el cual la maneje como aCb   pero es exactamente lo mismo 😛

(lo empecé a hacer en nasm para aprender como se hacia desde 0 pero au no lo termino 😐   )asi que estuve investigando y me encontre con lo que la mayoria de mis compañeros estuvo trabajando «traducir» el codigo a  lenguaje ensamblador con las herramientas de gcc, con la instruccion gcc -S archivo.c, con esto se genera un archivo.s el cual ya esta hecho en ensamblador con las caracteristicas de nuestro equipo; cuando estuve batallando con mi programa en nasm :p encontré ciertos documentos en los que puedo resaltar esta información.

El compilador gcc se puede ejecutar con varias opciones.

-o    indica el nombre del archivo de salida, cuando no se usa, el compilador genera un archivo llamado a.out por defecto.
-S   ensambla, la salida es un archivo en lenguaje ensamblador con el sufijo
-c    enlaza, la salida será un archivo en código objeto con el sufijo .o su contenido se encuentra en binario. Se puede utilizar la  herramienta objdump para desensamblar este archivo y visualizar su contenido.
-O opción de optimización, se acompaña con un número que indica el nivel de optimización deseado.
Se puede escoger entre O0, O1, O2 u O3.
-g produce información para la depuración del programa.

Usando el programa el comando:

gcc C.c -O3 -S

tambien se genera un archivo .s

Existe otra opción de (-v, verbose), la cual nos permite observar la manera en la
que gcc invoca a cada una de las herramientas mencionadas (el compilador, el
ensamblador y el enlazador) para generar el archivo ejecutable. Por ejemplo, si
repetimos la compilación del archivo ejemplo.s utilizando esta opción, podemos
distinguir cada uno de los pasos descritos anteriormente:

gcc C.s -o C.s -v

En este caso, se pueden observar las siguientes líneas dentro de la salida
producida por gcc:

Usando especificaciones internas.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6.1/lto-wrapper
Objetivo: i686-linux-gnu
Configurado con: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.1-9ubuntu3' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++,go --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
Modelo de hilos: posix
gcc versión 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3)
COLLECT_GCC_OPTIONS='-o' 'C.s' '-v' '-mtune=generic' '-march=i686'
 as --32 -o /tmp/ccWrfl7s.o C.s
COMPILER_PATH=/usr/lib/gcc/i686-linux-gnu/4.6.1/:/usr/lib/gcc/i686-linux-gnu/4.6.1/:/usr/lib/gcc/i686-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6.1/:/usr/lib/gcc/i686-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/i686-linux-gnu/4.6.1/:/usr/lib/gcc/i686-linux-gnu/4.6.1/../../../i386-linux-gnu/:/usr/lib/gcc/i686-linux-gnu/4.6.1/../../../../lib/:/lib/i386-linux-gnu/:/lib/../lib/:/usr/lib/i386-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/i686-linux-gnu/4.6.1/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'C.s' '-v' '-mtune=generic' '-march=i686'
 /usr/lib/gcc/i686-linux-gnu/4.6.1/collect2 --build-id --no-add-needed --as-needed --eh-frame-hdr -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -z relro -o C.s /usr/lib/gcc/i686-linux-gnu/4.6.1/../../../i386-linux-gnu/crt1.o /usr/lib/gcc/i686-linux-gnu/4.6.1/../../../i386-linux-gnu/crti.o /usr/lib/gcc/i686-linux-gnu/4.6.1/crtbegin.o -L/usr/lib/gcc/i686-linux-gnu/4.6.1 -L/usr/lib/gcc/i686-linux-gnu/4.6.1/../../../i386-linux-gnu -L/usr/lib/gcc/i686-linux-gnu/4.6.1/../../../../lib -L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/i686-linux-gnu/4.6.1/../../.. /tmp/ccWrfl7s.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-linux-gnu/4.6.1/crtend.o /usr/lib/gcc/i686-linux-gnu/4.6.1/../../../i386-linux-gnu/crtn.o
karenalduncin@lilalduncin:~$

Se observa que el gcc llevó a cabo tres pasos:

as --32 -o /tmp/ C.s

Llamó al compilador (cc1) pasando el archivo “C.c” como entrada, y
generando el archivo “ccWrfl7s.o” como salida. Llamó al ensamblador (as), pasando la salida del compilador como entrada, y generando el archivo objeto relocalizable “cc4ziThv.o” como salida.
Llamó al enlazador (collect2) pasando como entrada la salida del ensamblador y un conjunto de bibliotecas estándar, y generando como salida el archivo objeto ejecutable “comb” Luego de observar este resultado, es importante aclarar algunos puntos:
El archivo fuente original puede utilizarse directamente como entrada al compilador. En caso de que la entrada haya sido pre procesada previamente, debe utilizarse la opción –fpreprocessed para indicarle este hecho al cc1. El enlazador real es ld pero gcc hace la llamada a ollect2 debido al funcionamiento interno de esta herramienta.
Cada una de las herramientas llamadas por gcc cuenta con su propio conjunto de opciones que permiten un mayor control sobre todo el proceso de traducción en caso de ser necesario.
Finalmente, es posible realizar todos los pasos del proceso de traducción de manera manual, llamando a cada una de las herramientas con las opciones más adecuadas de acuerdo a lo que se quiere lograr. Por ejemplo, para generar el archivo ejecutable “Combb” podemos realizar  los siguientes pasos:

cpp C.c -o C.i
as C.s -o C.o

aqui se crea el fichero que contiene la dirección del archivo  lo podemos abrir con nuestro editor de texto que utilizemos.

En la vida real se dan situaciones en que se nos proporciona una biblioteca previamente compilada (.o, .a, .so) sin ningún tipo de  documentación, y debemos averiguar la forma correcta de utilizarla dentificarlas de alguna manera, bien sea obteniendo una lista de los símbolos globales que identifican a dichas funciones (entry points) o bien sea desensamblando el código binario y analizando el mismo.

Es posible llevar a cabo estas tareas utilizando un grupo de herramientas provistas por GNU para el manejo de archivos binarios, conocidas como binutils, entre las cuales se encuentran:

ld                         El enlazador GNU
as                         El ensamblador GNU
addr2line           Permite convertir direcciones a nombres de archivo y números de línea
ar                         Herramienta para el manejo de bibliotecas estáticas
nm                      Lista los símbolos contenidos en un archivo objeto
objcopy              Permite convertir entre formatos de archivo objeto
objdump           Muestra la información contenida en un archivo objeto
readelf               Extrae la información contenida en un archivo objeto en formato ELF
size                     Muestra el tamaño de los segmentos de un archivo objeto o una biblioteca estática
strings               Lista todas las cadenas imprimibles de un archivo
strip                   Elimina símbolos y secciones de un archivo objeto

De esta forma, podemos utilizar la herramienta objdump para desensamblar el segmento de código del archivo binario ejemplo.o y analizar su contenido:

karenalduncin@lilalduncin:~$ gcc C.c -O2 -c -o Com.o
C.c: En la función ‘main’:
C.c:14:8: aviso: se descarta el valor de devolución de ‘scanf’, se declaró con el atributo warn_unused_result [-Wunused-result]
C.c:16:8: aviso: se descarta el valor de devolución de ‘scanf’, se declaró con el atributo warn_unused_result [-Wunused-result]
karenalduncin@lilalduncin:~$ objdump C.o -d
//debajo les aparecera el desensamblado :B

En la cual se identifica claramente el procedimiento main y las instrucciones que conforman el cuerpo del mismo.
La herramienta objdump tiene varias opciones entre las que vale la pena destacar:
-d desensambla el código del segmento de texto.
-D desensambla todo el programa incluyendo los datos (el programa trata de traducir toda la información como instrucciones así que en la parte de datos la traducción no se corresponde con la información almacenada).
-G muestra la información de depuración de programas.
-l muestra los números de línea.
-r muestra las entradas de relocalización.
-R muestra las entradas de relocalización dinámica.
-t muestra la tabla de símbolos.
Si se incluye información de depuración del archivo binario (utilizando la opción –g), es posible relacionar cada dirección del programa desensamblado con las líneas originales del programa en lenguaje de alto nivel, mediante la utilización de addr2line:

gcc C.c -o C-o -c -g -O2
karenalduncin@lilalduncin:~$ addr2line -e C.o -s

Algunas opciones importantes de esta herramienta son:
-e permite especificar el archivo de entrada a analizar.
-s evita que se imprima la ruta completa del archivo en lenguaje de alto nivel al consultar una dirección.
-f Imprime el nombre de la función a la cual corresponde la línea del programa.Para archivos binarios que utilicen el formato ELF, podemos utilizar la herramienta readelf. La misma permite analizar toda la información adicional almacenada en el archivo, como por ejemplo la tabla de símbolos o la información de relocalización:

readelf C.o –r

Siguiente Imagen.

Esta herramienta también permite examinar el contenido de cualquier sección o encabezado del programa. Por ejemplo, podemos visualizar las características de todas las secciones, y observar el contenido de la sección de datos de solo lectura:

readelf C.o –S

Siguiente Imagen.

Clave para Opciones:

W (escribir), A (asignar), X (ejecutar), M (mezclar), S (cadenas)
I (info), L (orden enlazado), G (grupo), x (desconocido)
O (se requiere procesamiento extra del SO) o (específico del SO)
p (específico del procesador)

readelf C.o –x 5

Las principales opciones de readelf son las siguientes:
-a muestra toda la información contenida en el archivo
-h muestra la información de encabezado del archivo ELF
-S muestra la información de los encabezados de sección
-s muestra la tabla de símbolos
-r muestra la información de relocalización
-x vacía el contenido de la sección especificada

:c
Referencias
http://assembly-marcelinha.googlecode.com/files/EnsAmelia.pdf
http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html
http://www.itch.edu.mx/academic/industrial/sabaticorita/_private/07Combinaciones.htm

este ya es el codigo en ensamblador

http://pastebin.com/9zgZXtJb

y este fue el de nasm que aun no eh terminado D:

http://pastebin.com/uGmYzPYd


			

Alarma de Ladrones

Para elaborar una alarma, es necesario un diseño previo; en este caso usado con permiso de Robert Delp Electronics, éste incluye muchas de las características populares de las unidades comerciales. Estas características incluyen un retardo de entrada y salida, un corte automático del timbre y de control de relés para manipular cualquier dispositivo de señalización; además de componentes diversos, y materiales que requieren de un determinado manejo como por ejemplo el circuito integrado (CMOS 4001)el cual es muy sensible a la estática del cuerpo humano, por consiguiente, es necesario utilizar pinzas especiales (pueden ser de acrílico o pinzas de metal previamente desmagnetizadas)para no dañar dicho componente.

 Los detalles operativos de la alarma de ladrones caseros son los siguientes:

1. Fuente de Alimentación.- Se necesita una corriente de 1 microamperio (A), para controlar la mayoría de los dispositivos de señal.

2. Retardo de salida.-Retardo de 30 segundos (Nota: Incrementar el tamaño del capacitor, incrementa el retardo)

3. Retardo de entrada.-Retardo de 30 segundos para regresar a su estado de listo.

4. Corte Automático del timbre.-La señal audible se corta después de 6 minutos.

5. Reset automático.-Después del corte automático de la campana, la alarma volverá al estado de «listo», hasta que se active de nuevo.

Especificaciones del sistema

Componentes:

B1 Batería de Linterna de 12V (sustituibles por -eliminador de voltaje de 12V -dos pilas de 6V)

C1, C2 Capacitor electrolítico de 47µF, 16V

C3 Capacitor de disco de 0,01 µF

C4 Capacitor electrolítico de 470µF, 16V

D1,D2 Diodo 1N4148 (o 1N914)

D3 Diodo de silicio 1N4001, 1 A , 50 PIV

IC1 IC cuad. (cuatro salidas)NOR CMOS 4001

Q1 Transistor de NPN, 2N2222

R1,R3,R7,R8,R11,R12 (6) Resistencias de 10 kΩ, ½ W

R2 Resistencia de 22 MΩ, ½ W

R4,R5,R10 Resistencia de 680 kΩ, ½ W

R6 Resistencia de 100 kΩ, ½ W

R9 Resistencia de 220 Ω, ½ W

RL1 Relé, bobina 12V RS-1210

S1 Conmutador SPDT (sustituido por switch 2 polos un tiro)

Adicionalmente:

L LED Ultra (Azul)

R Resistencia de 380 Ω, ½ W

Circuito

Batería 12 V                                  Capacitor 47µF, 16V

                            

Alimentará la energía necesaria en el circuito.        Efectuará el retardo, ya sea el de salida, o de entrada

                                                                                                             (cambiando dicho capacitor, podremos aumentar el retardo).

Capacitor de Disco 0.001(103)                        Capacitor 470 µF, 16V

                       

Sirve para limitar cualquier cambio de voltaje.                                      Efectuará el retardo, ya sea el de salida o de entrada (cambiando dicho capacitor podremos aumentar el retardo).

 Relé RAS -1210 (5pines)                           CMOS 4001


Hará el cambio de estado de acuerdo al funcionamiento.           Es el Circuito integrado para poder realizar los flip-flops, por medio de compuertas NOR.

Diodo 4148                      Diodo 1N4001

Un diodo es un dispositivo que permite el paso de la corriente eléctrica en una única dirección. De forma simplificada, la curva característica de un diodo consta de dos regiones, por debajo de cierta diferencia de potencial, se comporta como un circuito abierto (no conduce), y por encima de ella como un circuito cerrado con muy pequeña resistencia eléctrica.

Mini Push button                      Interruptor Dip Switch de 2 vías

                   

Actuará como la compuerta normalmente abierta (NO)         Actuará como la compuerta normalmente cerrada (NC)

Buzzer 5 volts                  LED ultra              Resistencias

                            

Emite el sonido de la alarma              Enciende al momento de que se activa la alarma.                Sirven para regular el paso de la corriente.

Transistor de NPN 2N2222 

Funciona como un interruptor que modula y controla la corriente que se le está aplicando desde la batería.

Para realizar nuestra alarma debemos de seguir exactamente el circuito y utilizar los componentes correctos antes mencionados para su elaboración.

Al seguir las indicaciones de dicho circuito nos podremos dar cuenta de que las resistencias utilizadas tienen la especificación en su potencia y ésta viene mencionada a ½ Watt; lo antes citado tiene que respetarse, de esta forma concluiremos que este material conjuntamente con el capacitor de disco son los únicos materiales que utilizaremos con la peculiaridad de no tener polaridad.

Después de haber concluido de colocar los componentes del circuito en un protoboard; tenemos que seguir los siguientes pasos:

1. Verificar que todo el circuito tenga continuidad (esto se realizara mediante el apoyo de un multímetro.

2. Comprobar si hemos colocado correctamente los componentes (de acuerdo con las imágenes anexadas anteriormente).

3. Asegurarse de que la batería emita los 12Volts y también de que sus conexiones se encuentren en buen estado.

Al momento de comprobar esto usted puede permitir el paso de corriente por medio del switch (dos polos un tiro), realizado esto su alarma se encuentra en estado de «listo», a continuación puede activar la alarma a través de las compuertas normalmente abierta y normalmente cerradas (push button y dip switch respectivamente), un ejemplo de cómo emplear estas compuertas en una residencia podría ser:

-Normalmente abierto (NO).- debajo de un tapete en la entrada de una puerta (si se desea colocar en varias puertas es necesario colocarlo en paralelo).

-Normalmente Cerrado (NC).- colocar en las ventanas de la casa (estas se tienen que colocar en serie para asegurar su funcionamiento).

Asi queda el protoboard 😀

Costos de los componentes

Componentes

Unidad

Costo Unitario

Costo Total

Batería de linterna a 12V

1

$200.00

$200.00

Capacitor Electrolítico de 47 µF a 16V

2

$3.00

$6.00

Capacitor de disco cerámico de 0.01 µF

1

$1.50

$1.50

Capacitor Electrolítico de 470 µF a 16V

1

$8.00

$8.00

Diodo 1N4148 o (1N914)

2

$1.00

$2.00

Diodo de Silicio 1N4001,1A,50 PIV

1

$2.00

$2.00

IC quad(Cuatro Salidas) NOR CMOS 4001

1

$10.50

$10.50

Transistor NPN 2N2222

1

$3.00

$3.00

Resistencia de 10 KΩ, 1/2 Watt

6

$6.00

$36.00

Resistencia de 22 MΩ, 1/2 Watt

1

$2.00

$2.00

Resistencia de 680 KΩ, 1/2 Watt

3

$3.00

$9.00

Resistencia de 100 KΩ, 1/2 Watt

1

$1.00

$1.00

Resistencia de 220 Ω, 1/2 Watt

1

$1.00

$1.00

Relevador (Relé) bobina a 12V RS-1210

1

$15.00

$15.00

Conmutador SPDT (Switch 2 polos un tiro)

1

$8.00

$8.00

LED Ultra(Azul)

1

$10.00

$10.00

Resistencia de 380 Ω,1/2 Watt

1

$1.00

$1.00

Subtotal

$316.00

Total

$366.56

 Complicaciones al formar el circuito

Algunas piezas fueron difíciles de conseguir, a continuación pondremos la lista de dichas piezas:

Resistencia de 22 MΩ, ½ W: en ninguna tienda de electrónica la manejaban así que la encargamos sobre pedido.

-CMOS:  también se incluyo en el pedido.

-Capacitores: no tenían disponibles de 47µF a 16V.

Otras de las complicaciones que tuvimos al realizar el circuito fue que el momento de la duración de la alarma accionada, debería ser de 6 minutos, y se desactivaba a los 4 minutos con 40 segundos, lo que realizamos para solucionar esto, actualizamos a un nuevo CMOS, y la alarma funcionó adecuadamente; se agregó un capacitor de disco de 0.01 µF para que el retardo de entrada se aumentara, ya que se activaba antes de los 30 segundos.

Recomendaciones

Se podría trabajar con componentes menos sensibles como el Flip-Flop JK, o con un GalV816 programado para realizar la función del CMOS(las compuertas NOR). 

************************************************************************************************

Y ya soldado 🙂

**le falta el TTL :B pero esta en una de las bases / en la siguiente base se encuentran las compuertas NO/NC.

Tambien trabajaré este mismo prototipo implementado en un PIC16 con programacion en BASIC

🙂

Bibliografía

http://www.alldatasheet.com/

Lenguajes Alto y Bajo Nivel

Lenguaje de Máquina Lenguaje mas básico, propio de cada computadora, ya que está relacionado con el diseño del hardware de la misma (dependiente de la máquina)

PC->Lenguaje maquina -> Lenguaje Ensamblador ->Lenguaje de Alto nivel->Usuario

Lenguaje Ensamblador Consiste en abreviaturas similares al inglés, llamadas instrucciones nemotécnicas, que permiten representar las operaciones elementales de la computadora (dependiente de la máquina)

Lenguaje Ensamblador Lenguaje de bajo nivel o ensamblador : La computadora no entiende    directamente lenguaje ensamblador por lo que un programa escrito en este lenguaje tiene que ser  traducido a lenguaje de máquina por un programa llamado un ensamblador para que pueda ser  ejecutado por la computadora. Los lenguajes ensambladores todavía requieren que el programador tenga un buen conocimiento de la arquitectura de la computadora. Como los lenguajes ensambladores son dependientes de la máquina, todo programa escrito en un lenguaje ensamblador particular tendrá que ser reescrito si se va a ejecutar en otro tipo de computadora.

Los lenguajes de alto nivel, son aquellos cuya característica principal, consiste en una estructura sintáctica y semántica legible, acorde a las capacidades cognitivas humanas. A diferencia de los lenguajes de bajo nivel, son independientes de la arquitectura del hardware, motivo por el cual, asumen mayor portabilidad.

Son ejemplo de lenguajes de alto nivel: Python, Perl, PHP, Ruby, Lisp, Java, Fortran, C++, C#, entre otros.

 Podemos «traducir» /»ensamblar» códigos en lenguajes de alto nivel hacia los lenguajes ensambladores como por ejemplo:

Comando para ensamblar un codigo en C y utilizarlo en assembler :

codigo.c gcc -S

Tambien podemos utilizar el código de algún lenguaje de alto nivel a lenguaje ensamblador y de ahi a lenguaje maquina para realizar funciones con dispositivos programables.

para poder entender mejor el lenguaje ensamblador esto puede ser de gran ayuda.

Referencias

 

Cuando se utilizan Ciclos esto tambien es importante …

 

 

 

Referencias-

http://www.debianhackers.net/entendiendo-los-lenguajes-de-programacion

http://forum.codecall.net/assembly-tutorials/40086-intro-win32-assembly-using-nasm-part-2-a.html

Usabilidad

“la usabilidad es estar realmente seguro de que algo funciona bien: que una persona con habilidades promedio pueda utilizar una cosa para su intención sin terminar frustrado.”

La usabilidad es hoy en día reconocida como uno de los atributos de calidad dentro del desarrollo de software.

Los resultados de una buena usabilidad en un proyecto implican:

  • Beneficios para el usuario: permite al usuario acceder, de manera eficiente y satisfactoria, a un producto fácil de usar e intuitivo.
  • Beneficios para el cliente: Return on Investment (ROI); es decir, una reducción del tiempo y los costos de desarrollo, el mantenimiento, soporte y uso.

Mejora en la calidad del producto, haciéndolo más competitivo en un mercado que demanda mayor facilidad en el uso.

Hacer usabilidad es hacer que el usuario se enfoque en su tarea y no en la tecnología ni los controles que le permiten realizarla.

:3

Referencias-

http://www.fluxit.com.ar/servicios/usabilidad

http://www.intuitivamente.com/2011/11/10/usabilidad/

Sistemas Integrados

Un sistema integrado  (embedded system) es un sistema diseñado por lo general dedicado a la realización de funciones limitadas de computación fiable, segura y con costes de mantenimiento mínimos para hacer uno o varios dedicadas o especificas Funciones. Es incorporado como parte de un dispositivo completo que incluye hardware y partes mecánicas. Por el contrario, una computadora de propósito general, tales como unordenador personal  (PC), está diseñado para ser flexible y para cumplir con una amplia gama de necesidades del usuario final.Los sistemas empotrados controlar muchos dispositivos en uso común hoy en día. Los sistemas empotrados son controlados por uno o más núcleos de procesamiento principales, que son  micro controladores o procesador digital de señales (DSP). La característica clave, sin embargo, se ha dedicado a manejar una tarea particular. Dado que el sistema integrado está dedicado a tareas específicas, los ingenieros de diseño puede optimizar para reducir el tamaño y coste del producto y aumentar la fiabilidad y el rendimiento. Físicamente, sistemas integrados van desde los dispositivos portátiles, como los relojes digitales y reproductores de MP3 , a las grandes instalaciones fijas como semáforos, los controladores de fábrica , o los sistemas de control de las centrales nucleares . La complejidad varía de baja, con un solo microcontrolador  chip, a muy alto con varias unidades, periféricos y redes. En general, un «sistema integrado» no es un término estrictamente definido, como la mayoría de los sistemas tienen algún elemento de extensibilidad o de programación. Por ejemplo, los ordenadores portátiles comparten algunos elementos con los sistemas embebidos, tales como los sistemas operativos y microprocesadores que les de poder, pero que permiten diferentes aplicaciones que se cargan y periféricos que se conectan.

Los sistemas integrados abarcan todos los aspectos de la vida moderna y hay muchos ejemplos de su uso. Los sistemas de telecomunicaciones emplean a numerosos sistemas integrados de centrales telefónicas de la red de telefonía móvil. Las redes de computadoras utiliza dedicada enrutadores y los puentes de la red para la ruta de datos. Electrónica de consumo  incluyen asistentes personales digitales (PDA s), reproductores MP3, teléfonos móviles, consolas de videojuegos, cámaras digitales, DVD s, GPS  receptores, y la impresora.Muchos aparatos del hogar, como hornos de microondas, lavadora y el lavavajillas, están incluidos los sistemas integrados para proporcionar flexibilidad, eficiencia y características; sistemas de uso en red del termostato de control de temperatura más preciso y eficiente que puede cambiar la hora del día y de temporada . Domótica  utiliza con cable e inalámbricas de redes que pueden se utiliza para controlar las luces, el clima, seguridad, audio / visual, vigilancia, etc, todos los que utilizan los dispositivos integrados para la detección y el control. Los sistemas de transporte en el vuelo a los automóviles utilizan cada vez más sistemas embebidos. Los aviones nuevos contienen avanzados de aviónica como de guía inercial y los sistemas de GPS  receptores que también tienen considerables necesidades de seguridad. Varios motores eléctricos – motores DC sin escobillas, motor de inducción de, y motor de corriente continua, está utilizando eléctrico / electrónico de control del motor. Automóviles, vehículo eléctrico, y el vehículo híbrido s está utilizando cada vez más sistemas integrados para maximizar la eficiencia y reducir la contaminación.

En segundo lugar, los procesadores integrados se puede dividir en dos grandes categorías: los microprocesadores comunes (microprocesador) y microcontroladores (μC), que tienen muchos más periféricos en el chip, reduciendo el coste y tamaño.En contraste a la computadora personal y los mercados de servidores, un número bastante grande de arquitecturas de CPU que se han utilizado, hay Von Neumann  , así como diversos grados de la arquitectura de Harvard s, RISC, así como la no-RISC y VLIW; longitudes de palabra varían de 4 – bits a 64 bits y más allá (sobre todo en DSP  procesadores), aunque siguen siendo los más típicos 8/16-bit. La mayoría de las arquitecturas vienen en un gran número de diferentes variantes y formas, muchas de las cuales también son fabricados por varias compañías. Una larga lista, pero todavía no exhaustiva de las arquitecturas más comunes son: 65816, 65C02, 68HC08, 68HC11, 68k , 78K0R/78K0, 8051 , ARM , AVR , AVR32 , Blackfin ,C167 , Coldfire, la COP8 , Cortus APS3, EZ8 , eZ80, FR-V , H8, HT48 , M16C, M32C, MIPS , MSP430, PIC , PowerPC , R8C , RL78, SHARC , SPARC , ST6 , SuperH , TLCS-47, TLCS-870, TLCS-900, TriCore , V850 , 86 , XE8000 , Z80, AsAP , etc

Características que se pueden tomar en cuenta para un sistema integrado.

  1. Eficiencia Energética (llamar la potencia mínima para el propósito)
  2. Personalizados de tensión / potencia requisitos (VDC: 12, 14, 24, 72 ..)
  3. Seguridad (a prueba de hackers)
  4. Fiabilidad (trabajo sin fallo desde hace años)
  5. Medio Ambiente (amplio rango de temperatura, sellado de los productos químicos, la radiación)
  6. La interacción eficaz con el usuario (menos botones, el tacto)
  7. Se integre con el diseño

    Ejemplo un video de un proyecto que representa un sistema integrado 🙂

Al igual que con otro software, diseñadores de sistemas integrados utilizan  el compiladores, ensambladores, y depuradores para desarrollar software de sistemas embebidos. Sin embargo, también pueden utilizar algunas herramientas más específicas:

  • En los depuradores de circuito o emuladores (ver próxima sección).
  • Utilidades para agregar una suma de comprobación o CRC  de un programa, por lo que el sistema embebido puede comprobar si el programa es válido.
  • Para los sistemas que utilizan el procesamiento de señales digitales , los desarrolladores pueden utilizar un banco de trabajo de matemáticas, como Scilab  / Scicos , MATLAB  / Simulink , EICASLAB , MathCad , Mathematica , o DSP piedra variable para simular las matemáticas. También pueden utilizar las bibliotecas, tanto para el anfitrión y el objetivo que elimina el desarrollo de rutinas de DSP como se hace en RTOS DSPnano  y el sistema operativo Unison.
  • Compiladores y enlazadores de encargo puede ser utilizado para mejorar la optimización para el hardware en particular.
  • Un sistema embebido puede tener su propio lenguaje o herramienta de diseño, o añadir mejoras a un lenguaje ya existente, como adelante o básico .
  • Otra alternativa es agregar un sistema operativo en tiempo real  o el sistema operativo integrado, que pueden tener capacidades DSP como RTOS DSPnano .
  • El modelado y generación de código de herramientas  basa a menudo en las máquinas de estado

Las herramientas de software puede venir de varias fuentes:

  • Las compañías de software que se especializan en el mercado integrado
  • Portadas de los GNU  herramientas de desarrollo de software
  • A veces, las herramientas de desarrollo para un ordenador personal se puede utilizar si el procesador integrado es un pariente cercano a un procesador de PC común

A medida que la complejidad de los sistemas embebidos crezca, mayores herramientas a nivel de sistemas operativos y están migrando en una máquina donde tiene sentido. Por ejemplo, los teléfonos celulares, Personal Digital Assistant s y otras computadoras de los consumidores a menudo necesitan software que sea importante que se compra o proporcionados por una persona que no sea el fabricante de la electrónica. En estos sistemas, un ambiente de programación de código abierto como Linux , NetBSD , OSGi  o Java Embedded  es necesario para que el proveedor de software de terceros puede vender a un mercado grande.

Embedded depuración  puede realizarse en diferentes niveles, dependiendo de las instalaciones disponibles. Más simple al más sofisticado que puede ser más o menos agrupados en las siguientes áreas:

  • Depuración residente interactivo, utilizando el shell simple que ofrece el sistema operativo embebido (por ejemplo, Forth y Basic)
  • Depuración externo mediante la tala o la salida de puerto serie para rastrear la operación utilizando un monitor en flash o utilizando un servidor de depuración como el depurador remedio  que incluso trabaja para heterogéneos sistemas multi-núcleo.
  • Un depurador en circuito (ICD), un dispositivo de hardware que se conecta con el microprocesador a través de unJTAG  o interfaz de Nexus. Esto permite el funcionamiento del microprocesador para ser controlado externamente, pero normalmente se limita a las capacidades de depuración específicos en el procesador.
  • Un emulador in-circuit  (ICE) reemplaza el microprocesador con un equivalente simulada, proporcionando un control total sobre todos los aspectos del microprocesador.
  • Un completo emulador  ofrece una simulación de todos los aspectos del hardware, permitiendo a todos a ser controlada y modificada, y que permite la depuración en un PC normal.

Los sistemas empotrados suelen residir en las máquinas que se espera que funcione continuamente durante años, sin errores, y en algunos casos se recuperan por sí mismos si ocurre un error. Por lo tanto el software está desarrollado y probado por lo general con más cuidado que el de los ordenadores personales, y poco fiables partes mecánicas en movimiento, tales como unidades de disco, interruptores o botones que se evitan. temas específicos de confiabilidad pueden incluir:

  1. El sistema de seguridad no puede ser cerrado para su reparación, o es demasiado inaccesible para reparar. Los ejemplos incluyen sistemas espaciales, cables submarinos, balizas de navegación, sistemas de perforaciones, y los automóviles.
  2. El sistema debe mantenerse en funcionamiento por razones de seguridad. «Modos de Limp» son menos tolerables. A menudo copias de seguridad son seleccionados por un operador. Los ejemplos incluyen la navegación aérea, sistemas de control del reactor, los controles de seguridad críticos fábrica de productos químicos, las señales de los trenes, motores de aviones monomotores.
  3. El sistema se pierden grandes cantidades de dinero cuando se cierran: centrales telefónicas, controles de fábrica, puentes y los controles del ascensor, la transferencia de fondos y creación de mercado, las ventas automáticas y el servicio.

Herramientas que probablemente haré uso en este curso.

Oregano (Simulador de Circuitos Electronicos)

sudo apt-get install oregano
/*aun no eh instalado las librerias ._. asi que no compila nada,agradeceria si alguien me podría orientar */

 

Arduinio (herramientas para el compilador /openjdk/IDE arduino)

sudo apt-get install gcc-avr
sudo apt-get install openjdk-6-jre 
sudo apt-get install arduino

 

Referencias
http://www.absoluteastronomy.com/topics/Embedded_system
http://www.embeddedsystem.com/
http://www.absoluteastronomy.com/topics/Home_automation
http://www.arduino.cc/

LaTeX

LaTeX tiene una variedad de paquetes que pueden ayudar a los algoritmos de formato, el código, y «pseudo». Estos paquetes proporcionan mejoras de estilo en un estilo uniforme (es decir, las fuentes máquina de escribir) para que las construcciones tales como bucles o condicionales se separa visualmente del resto del texto.

LaTeX está formado por un gran conjunto de macroinstrucción de es un sistema de tipografia ( \mathbf{T\!_{\displaystyle E} \! X}). Es muy utilizado para la composición de artículos académicos, tesis y libros técnicos, dado que la calidad tipográfica de los documentos realizados con LaTeX es comparable a la de una editorial científica de primera línea.

LaTeX es software libre bajo licencia LPPL.

El nombre LaTeX, al derivarse del nombre TeX, mantiene la misma regla para la pronunciación que Donald Knuth especifica en The TeXbook,4 es decir que, en castellano, debería pronunciarse /látej/ pues la última letra no es la x (equis) sino la letra griega χ (ji). No obstante, la pronunciación viene dada por el uso, tal como explica Leslie Lamport en su libro,3 por lo que suele ser /láteks/ la manera más habitual de nombrarlo en español.

Composición tipográfica con el paquete de algoritmos

El entorno ofrece una serie de algoritmos de las construcciones populares para el diseño de algoritmos. El comando \ begin {} algorítmico se puede dar el argumento opcional de un entero positivo, que si se les hará la numeración de líneas que se produzca en los múltiplos de ese entero. Por ejemplo \ begin {} algoritmos [5] se incorpore al ambiente algorítmico y el número de cada quinta línea.

A continuación se muestra un ejemplo de un algoritmo de composición básica utilizando el paquete de algoritmos (recuerde añadir el \ usepackage {} algorítmica declaración a su preámbulo del documento):

\begin{algorithmic}
\IF {$i\geq maxval$}
        \STATE $i\gets 0$
\ELSE
        \IF {$i+k\leq maxval$}
                \STATE $i\gets i+k$
        \ENDIF
\ENDIF
\end{algorithmic}

La fuente de LaTeX se puede escribir en un formato familiar a los programadores para que sea fácil de leer. Esto no va, sin embargo, afectan al diseño final del documento.

Latex-algorithmic-if-else.png

** La palabra LaTeX en código

Código wiki

El código » <i>L<sup>A</sup>T<sub>E</sub>X</i>» genera LATEX

El código «L<sup>A</sup>T<sub>E</sub>X» genera LATEX

El código «<math>L^AT_EX</math>» genera: LATEX

Código LaTeX

El código «\LaTeX{}» genera el logo. Cuando no puede ser reproducido adecuadamente, por ejemplo al escribir en texto llano, se suelen escribir las consonantes en mayúsculas («LaTeX») para evitar la confusión con la palabra «látex».

Lenguaje

código símbolo código símbolo código símbolo código símbolo código símbolo
\’a á \’e é \'{\i} í \’o ó \’u ú
\’A Á \’E É \'{\I} Í \’O Ó \’U Ú
\»u ü \»U Ü \~n ñ \~N Ñ \c c ç
\c C Ç  !` ¡  ?` ¿
\AA Å \^a â \`a à \=a ā \»a ä
\~a ã \ae æ \oe œ \o ø ð

Alfabeto griego

código símbolo código símbolo código símbolo código símbolo
\Alpha \Alpha\, \Beta \Beta\, \Gamma \Gamma\, \Delta \Delta\,
\Epsilon \Epsilon\, \Zeta \Zeta\, \Eta \Eta\, \Theta \Theta\,
\Iota \Iota\, \Kappa \Kappa\, \Lambda \Lambda\, \Mu \Mu\,
\Nu \Nu\, \Xi \Xi\, \Pi \Pi\, \Rho \Rho\,
\Sigma \Sigma\, \Tau \Tau\, \Upsilon \Upsilon\, \Phi \Phi\,
\Chi \Chi\, \Psi \Psi\, \Omega \Omega\,
\alpha \alpha\, \beta \beta\, \gamma \gamma\, \delta \delta\,
\epsilon \epsilon\, \zeta \zeta\, \eta \eta\, \theta \theta\,
\iota \iota\, \kappa \kappa\, \lambda \lambda\, \mu \mu\,
\nu \nu\, \xi \xi\, \pi \pi\, \rho \rho\,
\sigma \sigma\, \tau \tau\, \upsilon \upsilon\, \phi \phi\,
\chi \chi\, \psi \psi\, \omega \omega\,

Símbolos matemáticos

código símbolo código símbolo código símbolo código símbolo
\digamma \digamma \varepsilon \varepsilon \varkappa \varkappa \varphi \varphi\,
\varpi \varpi \varrho \varrho \varsigma \varsigma \vartheta \vartheta
\aleph \aleph \beth \beth \daleth \daleth \complement \complement
\ell \ell \eth \eth \hslash \hslash \mho \mho
\partial \partial \wp \wp \infty \infty \angle \angle
\Finv \Finv \Game \Game \Im \Im \Re \Re
\exists \exists \forall \forall \in  \in \ni  \ni
\approx \approx \neq \neq \leq \leq \geq \geq
\leftarrow  \leftarrow \rightarrow  \rightarrow \langle  \langle \rangle  \rangle
\nabla  \nabla \mathbb{AB} \mathbb{AB} \mathcal{AB} \mathcal{AB} \mathbf{AB} \mathbf{AB}
\times  \times \emptyset  \emptyset \Rightarrow  \Rightarrow \hookrightarrow{}  \hookrightarrow{}
\cong  \cong \{ { \} } \subset  \subset
\prod  \prod \coprod  \coprod \bigcup  \bigcup \bigcap  \bigcap

Expresiones matemáticas

código resultado
0=a_{11} + a_{12} 0=a_{11} + a_{12}\,
x^{a+b}=x^ax^b x^{a+b}=x^ax^b\,
x_i=\sqrt[n]{\frac{a_i}{b_i}} x_i=\sqrt[n]{\frac{a_i}{b_i}}
\begin{pmatrix} \alpha& \beta^{*}\\ \gamma^{*}& \delta \end{pmatrix} \begin{pmatrix}\alpha& \beta^{*}\\ \gamma^{*}& \delta \end{pmatrix}
\int_{\vert x-x_0 \vert < X_0}\Phi(x) <img src=»http://upload.wikimedia.org/math/8/d/8/8d817aaba437909c63456ffd49706737.png» alt=»\int_{\vert x-x_0 \vert
\int\limits_{\vert x-x_0 \vert < X_0}\Phi(x) <img src=»http://upload.wikimedia.org/math/0/a/6/0a69ba57b97b6ca0575aa9784ab19790.png» alt=»\int\limits_{\vert x-x_0 \vert
\oint F(x)dx \oint F(x)dx
\iint \Phi(x, y)dxdy \iint \Phi(x,y)dxdy
\lim_{n \rightarrow \infty} \frac {n*l}{2*r} = \pi \lim_{n \rightarrow \infty} \frac {n*l}{2*r}=\pi
{n \choose r} = \frac{n!}{r! (n – r)!} {n \choose r} = \frac{n!}{r! (n - r)!}
x’+x» = \dot x + \ddot x  x'+x'' = \dot x + \ddot x
\vec \mathbf{v} = a\hat x + b\hat y \vec \mathbf{v}  = a\hat x + b\hat y
\begin{matrix}A\xrightarrow{\;\;\;f\;\;\;}B\\\pi\downarrow{\;\;\;\;\;}\;\;\;\uparrow{} \phi\\C\xrightarrow{\;\;\;g\;\;\;}D\end{matrix} \begin{matrix}A\xrightarrow{\;\;\;f\;\;\;}B\\\pi\downarrow{\;\;\;\;\;}\;\;\;\uparrow{} \phi\\C\xrightarrow{\;\;\;g\;\;\;}D\end{matrix}
f(x)=\begin{cases} \begin{matrix} 0 & si\; x>0 \\ x^2 & si\;no \end{matrix} \end{cases} 0 \\ x^2 & si\;no \end{matrix} \end{cases}» />
código
\documentclass[12pt]{article}
\usepackage[spanish]{babel}
\usepackage{amsmath}
\title{\LaTeX}
\date{}
% Este es un comentario, no será mostrado en el documento final.
\begin{document}
  \maketitle \LaTeX{} es un programa para preparar documentos con
  el sistema de tipograf\'{\i}as\footnote{%nota al pie de página
               Seg\'{u}n Wikipedia, la tipograf\'{i}a es el arte y t\'{e}cnica del manejo y selecci\'{o}n de tipos,
originalmente de plomo, para crear trabajos de impresi\'{o}n } %fin nota al pie de página
  \TeX{}. \LaTeX{} fue desarrollado originalmente por Leslie Lamport
  en 1984 y se convirti\'o en el m\'etodo dominante para la
  manipulaci\'on de \TeX. La versi\'on utilizada para generar
  este documento es \LaTeXe.
  \newline
  % El siguiente código muestra la calidad de la tipografía de LaTeX
  \begin{align}
    E &=& mc^2                              \\
    m &=& \frac{m_0}{\sqrt{1-\frac{v^2}{c^2}}}
  \end{align}
\end{document}
resultado
Ejemplo LaTeX.png

Single line statements

\STATE <text>
traslate

traducción del inglés al español

Una declaración simple, por ejemplo, para el establecimiento de una variable. Por ejemplo,
\begin{algorithmic} \STATE i=0 \end{algorithmic}

would produce
i = 0

If-statements

Hay tres formas de esta construcción

\IF{<condition>} <text> \ENDIF
\IF{<condition>} <text> \ELSE <text> \ENDIF
\IF{<condition>} <text> \ELSIF{<condition>} <text> \ELSE <text> \ENDIF

The third form accepts as many \ELSIF{} clauses as required.

For-loops

Hay dos formas

\FOR{<condition>} <text> \ENDFOR
\FORALL{<condition>} <text> \ENDFOR
Un tradicional bucle «for». El método de iteración se describe generalmente en el primer argumento,

e.g.

\FOR{$i = 1 \to 10$}
\STATE $i \gets i + 1$
\ENDFOR

While-loops

\WHILE{<condition>} <text> \ENDWHILE

Repeat until condition

\REPEAT <text> \UNTIL{<condition>}

Infinite loops

\LOOP <text> \ENDLOOP

Precondition

\REQUIRE <text>

Postcondition

\ENSURE <text>

Returning variables

\RETURN <text>

Printing variables

\PRINT <text>
This is included because it is used so frequently it is considered an operation in its own right.

Comentarios

\ {COMENTARIO <text>}

Tenga en cuenta que no puede utilizar COMENTARIO \ como la primera declaración de una estructura cerrada, como \ SI .. \ ENDIF \ PARA .. \ ENDFOR, \ forall .. \ ENDFORALL, \ MIENTRAS .. \ ENDWHILE, y \ begin {algorítmica} .. \ end {} algorítmica. Un error «Error LaTeX: algo va mal tal vez una falta \ item» serán reportados (No tiene mucho sentido). Hay dos soluciones:

Use \ COMENTARIO DEL ESTADO \ {} <text>.
Utilizar los argumentos opcionales en las estructuras cerradas. Por ejemplo, \ mientras que [<comment-text>] {} <condición>. Para utilizar las matemáticas en el texto del comentario, vuelva a colocar $ .. $ por \ {..} ensuremath

Compatibilidad con hyperref

Debido a un error, el paquete de algoritmos no es compatible con hyperref. Una solución es el paquete de algoritmos-fix. Copia el código que se encuentra en la página vinculada a un archivo llamado algorítmicafix.sty e incluirlo con \ usepackage {algorítmica, algorítmica-fix}. Sin embargo, si este truco no funciona, intente con usepackage \ {} hyperref antes de usar \ usepackage {} algorítmica. En este caso, puede que ni siquiera necesita el algoritmofix.sty.
Cambio de nombre de las cosas: el algoritmo de procedimiento, requieren / garantizar a la entrada / salida

\ floatname {algorithm} {} Procedimiento
\ renewcommand {\ algorithmicrequire} {\ textbf {Entrada:}}
\ renewcommand {algorithmicensure \} {\ textbf {Salida:}}

El medio ambiente algoritmo

A menudo es útil para el algoritmo producida por algoritmos que «flotaba» en el punto óptimo en el documento para evitar que se divide entre las páginas. El entorno ofrece este algoritmo y algunas otras funciones útiles. Incluirla añadiendo la
\usepackage{algorithm} to your document’s preamble. It is entered into by

\begin{algorithm} \caption{<your caption for this algorithm>} \label{<your label for references later in your document>} \begin{algorithmic} <algorithmic environment> \end{algorithmic} \end{algorithm}

algoritmo de numeración

El sistema de numeración por defecto para el paquete de algoritmos algoritmo es el número secuencial. A menudo no es deseable, sobre todo en documentos de gran tamaño, donde la numeración de acuerdo al capítulo es más adecuado. La numeración de los algoritmos pueden ser influenciados por proporcionar el nombre del componente del documento en el que la numeración debe ser reiniciado. Los valores permitidos para esta opción son: parte, capítulo, artículo, inciso, subsubsection o nada (por defecto). Por ejemplo:

\usepackage[chapter]{algorithm}

Lista de los algoritmos

Cuando se utiliza figuras o tablas, puede agregar una lista de ellos cerca de la tabla de contenidos; el paquete algoritmo proporciona un comando similar. sólo hay que poner

\ listofalgorithms

en cualquier parte del documento, y LaTeX se imprimirá una lista de los «algoritmo» ambientes en el documento con la página correspondiente y el título.

Un ejemplo del manual

Este es un ejemplo tomado del manual (manual oficial, p.7)

\begin{algorithm}                      % enter the algorithm environment
\caption{Calculate $y = x^n$}          % give the algorithm a caption
\label{alg1}                           % and a label for \ref{} commands later in the document
\begin{algorithmic}                    % enter the algorithmic environment
\REQUIRE $n \geq 0 \vee x \neq 0$
\ENSURE $y = x^n$
\STATE $y \Leftarrow 1$
\IF{$n < 0$} \STATE $X \Leftarrow 1 / x$ \STATE $N \Leftarrow -n$ \ELSE \STATE $X \Leftarrow x$ \STATE $N \Leftarrow n$ \ENDIF \WHILE{$N \neq 0$} \IF{$N$ is even} \STATE $X \Leftarrow X \times X$ \STATE $N \Leftarrow N / 2$ \ELSE[$N$ is odd] \STATE $y \Leftarrow y \times X$ \STATE $N \Leftarrow N - 1$ \ENDIF \ENDWHILE \end{algorithmic} \end{algorithm}

Referencias=:http://es.wikipedia.org/wiki/LaTeX

Algoritmo Floyd Warshall

Algoritmo Floyd Warshall


  • Obtiene la mejor ruta entre todo par de nodos.
  • Trabaja con la matriz D inicializada con las distancias directas entre todo par de nodos.
  • La iteración se produce sobre nodos intermedios, es decir, para todo elemento de la matriz se prueba si lo mejor para ir de i a j es a través de un nodo intermedio elegido o como estaba anteriormente, y esto se prueba con todos los nodos de la red.
    Una vez probados todos los nodos de la red como nodos intermedios, la matriz resultante da la mejor distancia entre todo par de nodos.

Es decir el algoritmo es el siguiente:

  • Iniciación.
    – matriz de distancias.
    – distancia del enlace entre el nodo i y el nodo j.
  • Iteración. Para n=0,1 … ,N-1

    Empezando con el nodo 1 como intermedio (n=0), se prueba con todos los nodos como nodos intermedios, el último es con el nodo N como nodo intermedio (n=N-1), y así se van hallando las distancias mínimas.

Árboles


Para el desarrollo de los siguientes conceptos vamos a tomar como ejemplo la siguiente red:

y sobre ella vamos a definir:

  • Arbol de expansión (Spanning tree)de una red es un subconjunto de la red en el que conseguimos:
    1. Mantener la conectividad; es posible alcanzar cualquier nodo desde otro.
    2. No hay lazos, no hay bucles topológicos.

    No es único; un ejemplo sobre la red anterior es:

  • Se suele usar cuando se desea hacer una inundación, ya que si todos los nodos tienen en su tabla de encaminamiento el mismo árbol de expansión, no se producirán bucles y todos los paquetes llegan a todos los nodos. Se usa sobre todo en difusiones.
  • Arbol de expansión de coste o distancia mínima
    Es un árbol de expansión que cumple que el sumatorio de distancias que emplea es mínimo.
    Por ejemplo el anterior tiene coste 10.
    El siguiente es mínimo ( su suma de distancias es 5);no existe otro con suma de distancias menor.
  • Arbol divergente de un nodo.
    Hay un árbol divergente por cada nodo sin embargo hay un árbol de expansión para toda la red.
    El árbol divergente de un nodo es aquel árbol que determina las distancias más cortas de ese nodo al resto de los nodos: es, expresada de forma gráfica, la tabla de encaminamiento de cada nodo.
    Por ejemplo, en la red usada como ejemplo, el árbol divergente del nodo A, es el siguiente:


Para el ejemplo anterior las matrices serían:


Nota:El nodo 1 es el nodo A de la red, el 2 es el B y así sucesivamente.
La segunda matriz tiene el nodo 1 como nodo intermedio, la tercera matriz tiene el nodo 2 como nodo intermedio, … y así hasta la última matriz que tiene el nodo 5 como nodo intermedio y da como resultado la matriz de distancias mínimas buscada.


  • La última matriz es la matriz de distancias buscada, ya que se han probado todos los nodos intermedios.
  • El algoritmo da sólo la menor distancia; se debe manejar información adicional para encontrar tablas de encaminamiento.
  • Hasta no hallar la última matriz no se encuentran las distancias mínimas.
  • Su complejidad es del orden de .

Encontre 2 videos que explican como realizarlo.

y mi ejemplo que realizamos en el salon de clases 😀

a travez de este grafo se usa el mismo metodo …

esto fue un poco dificil aun tengo dudas si lo tengo bien todavia falta corroborar agradeceria comentarios 😀

Referencias

http://ants.dif.um.es/asignaturas/redes/redes/tema3/floyd.htm

Arboles Binarios/Grafos/Estructura de Datos

Un árbol binario es una estructura de datos en la cual cada nodo siempre tiene un hijo izquierdo y un hijo derecho. No pueden tener más de dos hijos (de ahí el nombre «binario»). Si algún hijo tiene como referencia a null, es decir que no almacena ningún dato, entonces este es llamado un nodo externo. En el caso contrario el hijo es llamado un nodo interno. Usos comunes de los árboles binarios son los árboles binarios de búsqueda, los montículos binarios y Codificación de Huffman.

Definición de teoría de grafos

Un árbol binario sencillo de tamaño 9, 4 niveles y altura 3 (altura = máximo nivel – 1), con un nodo raíz cuyo valor es 2.

En teoría de grafos, se usa la siguiente definición: «Un árbol binario es un grafo conexo, acíclico y no dirigido tal que el grado de cada vértice no es mayor a 3». De esta forma sólo existe un camino entre un par de nodos.

Un árbol binario con enraizado es como un grafo que tiene uno de sus vértices, llamado raíz, de grado no mayor a 2. Con la raíz escogida, cada vértice tendrá un único padre, y nunca más de dos hijos. Si rehusamos el requerimiento de la conectividad, permitiendo múltiples componentes conectados en el grafo, llamaremos a esta última estructura un bosque..

Tipos de árboles binarios

Un árbol binario es un árbol con raíz en el que cada nodo tiene como máximo dos hijos.

Un árbol binario lleno es un árbol en el que cada nodo tiene cero o dos hijos.

Un árbol binario perfecto es un árbol binario lleno en el que todas las hojas (vértices con cero hijos) están a la misma profundidad (distancia desde la raíz, también llamada altura).

A veces un árbol binario perfecto es denominado árbol binario completo. Otros definen un árbol binario completo como un árbol binario lleno en el que todas las hojas están a profundidad n o n-1, para alguna n.

Un árbol binario es un árbol en el que ningún nodo puede tener más de dos subárboles. En un árbol binario cada nodo puede tener cero, uno o dos hijos (subárboles). Se conoce el nodo de la izquierda como hijo izquierdo y el nodo de la derecha como hijo derecho.

Recorridos sobre árboles binarios

Recorridos en profundidad

El método de este recorrido es tratar de encontrar de la cabecera a la raíz en nodo de unidad binaria. Ahora pasamos a ver la implementación de los distintos recorridos:

Recorrido en preorden

En este tipo de recorrido se realiza cierta acción (quizás simplemente imprimir por pantalla el valor de la clave de ese nodo) sobre el nodo actual y posteriormente se trata el subárbol izquierdo y cuando se haya concluido, el subárbol derecho. Otra forma para entender el recorrido con este metodo seria seguir el orden: nodo raiz, nodo izquierda, nodo derecha.

En el árbol de la figura el recorrido en preorden sería: 2, 7, 2, 6, 5, 11, 5, 9 y 4.

void preorden(tArbol *a)
{
  if (a != NULL) {
    tratar(a);                        //Realiza una operación en nodo
    preorden(a->hIzquierdo);
    preorden(a->hDerecho);
  }
}

Implementación en pseudocódigo de forma iterativa:

push(s,NULL);       //insertamos en una pila (stack) el valor NULL, para asegurarnos de que esté vacía
push(s,raíz);                       //insertamos el nodo raíz
MIENTRAS (s <> NULL) HACER
    p = pop(s);                     //sacamos un elemento de la pila
    tratar(p);                      //realizamos operaciones sobre el nodo p
    SI (D(p) <> NULL)               //preguntamos si p tiene árbol derecho
         ENTONCES push(s,D(p));
    FIN-SI
    SI (I(p) <> NULL)               //preguntamos si p tiene árbol izquierdo
         ENTONCES push(s,I(p));
    FIN-SI
FIN-MIENTRAS

 Recorrido en postorden

En este caso se trata primero el subárbol izquierdo, después el derecho y por último el nodo actual. Otra forma para entender el recorrido con este metodo seria seguir el orden: nodo izquierda, nodo derecha, nodo raiz. En el árbol de la figura el recorrido en postorden sería: 2, 5, 11, 6, 7, 4, 9, 5 y 2.

void postorden(tArbol *a)
{
  if (a != NULL) {
    postorden(a->hIzquiedo);
    postorden(a->hDerecho);
    tratar(a);                        //Realiza una operación en nodo
  }
}
Recorrido en inorden

En este caso se trata primero el subárbol izquierdo, después el nodo actual y por último el subárbol derecho. En un ABB este recorrido daría los valores de clave ordenados de menor a mayor. Otra forma para entender el recorrido con este metodo seria seguir el orden: nodo izquierda,nodo raiz,nodo derecha. En el árbol de la figura el recorrido en inorden sería: 2, 7, 5, 6, 11, 2, 5, 4, 9.

Esquema de implementación:

void inorden(tArbol *a)
{
  if (a != NULL) {
    inorden(a->hIzquierdo);
    tratar(a);                                 //Realiza una operación en nodo
    inorden(a->hDerecho);
  }
}

Grafos **

Historia

Puentes de Königsberg.

El trabajo de Leonhard Euler, en 1736, sobre el problema de los puentes de Königsberg es considerado el primer resultado de la teoría de grafos. También se considera uno de los primeros resultados topológicos en geometría (que no depende de ninguna medida). Este ejemplo ilustra la profunda relación entre la teoría de grafos y la topología.

En 1845 Gustav Kirchhoff publicó sus leyes de los circuitos para calcular el voltaje y la corriente en los circuitos eléctricos.

En 1852 Francis Guthrie planteó el problema de los cuatro colores que plantea si es posible, utilizando solamente cuatro colores, colorear cualquier mapa de países de tal forma que dos países vecinos nunca tengan el mismo color. Este problema, que no fue resuelto hasta un siglo después por Kenneth Appel y Wolfgang Haken, puede ser considerado como el nacimiento de la teoría de grafos. Al tratar de resolverlo, los matemáticos definieron términos y conceptos teóricos fundamentales de los grafos.

Estructuras de datos en la representación de grafos

Existen diferentes formas de almacenar grafos en una computadora. La estructura de datos usada depende de las características del grafo y el algoritmo usado para manipularlo. Entre las estructuras más sencillas y usadas se encuentran las listas y las matrices, aunque frecuentemente se usa una combinación de ambas. Las listas son preferidas en grafos dispersos porque tienen un eficiente uso de la memoria. Por otro lado, las matrices proveen acceso rápido, pero pueden consumir grandes cantidades de memoria.

Estructura de lista

Grafo de lista de adyacencia.

  • lista de incidencia – Las aristas son representadas con un vector de pares (ordenados, si el grafo es dirigido), donde cada par representa una de las aristas.1
  • lista de adyacencia – Cada vértice tiene una lista de vértices los cuales son adyacentes a él. Esto causa redundancia en un grafo no dirigido (ya que A existe en la lista de adyacencia de B y viceversa), pero las búsquedas son más rápidas, al costo de almacenamiento extra.

En esta estructura de datos la idea es asociar a cada vértice i del grafo una lista que contenga todos aquellos vértices j que sean adyacentes a él. De esta forma sólo reservará memoria para los arcos adyacentes a i y no para todos los posibles arcos que pudieran tener como origen i. El grafo, por tanto, se representa por medio de un vector de n componentes (si |V|=n) donde cada componente va a ser una lista de adyacencia correspondiente a cada uno de los vértices del grafo. Cada elemento de la lista consta de un campo indicando el vértice adyacente. En caso de que el grafo sea etiquetado, habrá que añadir un segundo campo para mostrar el valor de la etiqueta.

Estructuras matriciales

  • Matriz de incidencia – El grafo está representado por una matriz de A (aristas) por V (vértices), donde [arista, vértice] contiene la información de la arista (1 – conectado, 0 – no conectado)
  • Matriz de adyacencia – El grafo está representado por una matriz cuadrada M de tamaño n2, donde n es el número de vértices. Si hay una arista entre un vértice x y un vértice y, entonces el elemento mx,y es 1, de lo contrario, es 0.

Definiciones

Vértice

Artículo principal: Vértice (teoría de grafos)

Los vértices constituyen uno de los dos elementos que forman un grafo. Como ocurre con el resto de las ramas de las matemáticas, a la Teoría de Grafos no le interesa saber qué son los vértices.

Diferentes situaciones en las que pueden identificarse objetos y relaciones que satisfagan la definición de grafo pueden verse como grafos y así aplicar la Teoría de Grafos en ellos.

Grafo

Artículo principal: Grafo

En la figura, V = { a, b, c, d, e, f }, y A = { ab, ac, ae, bc, bd, df, ef }.

Un grafo es una pareja de conjuntos G = (V,A), donde V es el conjunto de vértices, y A es el conjunto de aristas, este último es un conjunto de pares de la forma (u,v) tal que u,v \in V, tal que u \neq v. Para simplificar, notaremos la arista (a,b) como ab.

En teoría de grafos, sólo queda lo esencial del dibujo: la forma de las aristas no son relevantes, sólo importa a qué vértices están unidas. La posición de los vértices tampoco importa, y se puede variar para obtener un dibujo más claro.

Muchas redes de uso cotidiano pueden ser modeladas con un grafo: una red de carreteras que conecta ciudades, una red eléctrica o la red de drenaje de una ciudad.

Subgrafo

Un subgrafo de un grafo G es un grafo cuyos conjuntos de vértices y aristas son subconjuntos de los de G. Se dice que un grafo G contiene a otro grafo H si algún subgrafo de G es H o es isomorfo a H (dependiendo de las necesidades de la situación).

El subgrafo inducido de G es un subgrafo G’ de G tal que contiene todas las aristas adyacentes al subconjunto de vértices de G.

Definición:

Sea G=(V, A). G’=(V’,A’) se dice subgrafo de G si:

1- V’ \subseteq V

2- A’ \subseteq A

3- (V’,A’) es un grafo

  • Si G’=(V’,A’) es subgrafo de G, para todo v \in G se cumple gr (G’,v)≤ gr (G, v)

G2 es un subgrafo de G.

Grafos1.jpg

Aristas dirigidas y no dirigidas

Grafo ejemplo 2.png

En algunos casos es necesario asignar un sentido a las aristas, por ejemplo, si se quiere representar la red de las calles de una ciudad con sus direcciones únicas. El conjunto de aristas será ahora un subconjunto de todos los posibles pares ordenados de vértices, con (a, b) ≠ (b, a). Los grafos que contienen aristas dirigidas se denominan grafos orientados, como el siguiente:

Las aristas no orientadas se consideran bidireccionales para efectos prácticos (equivale a decir que existen dos aristas orientadas entre los nodos, cada una en un sentido).

En el grafo anterior se ha utilizado una arista que tiene sus dos extremos idénticos: es un lazo (o bucle), y aparece también una arista bidireccional, y corresponde a dos aristas orientadas.

Aquí V = { a, b, c, d, e }, y A = { (a, c), (d, a), (d, e), (a, e), (b, e), (c, a), (c, c), (d, b) }.

Se considera la característica de «grado» (positivo o negativo) de un vértice v (y se indica como (v)), como la cantidad de aristas que llegan o salen de él; para el caso de grafos no orientados, el grado de un vértice es simplemente la cantidad de aristas incidentes a este vértice. Por ejemplo, el grado positivo (salidas) de d es 3, mientras que el grado negativo (llegadas) de d es 0.

Según la terminología seguida en algunos problemas clásicos de Investigación Operativa (p.ej.: el Problema del flujo máximo), a un vértice del que sólo salen aristas se le denomina fuente (en el ejemplo anterior, el vértice d); tiene grado negativo 0. Por el contrario, a aquellos en los que sólo entran aristas se les denomina pozo o sumidero (en el caso anterior, el vértice e); tiene grado positivo 0. A continuación se presentan las implementaciones en maude de grafos no dirigidos y de grafos dirigidos.En los dos casos, las especificaciones incluyen, además de las operaciones generadoras, otras operaciones auxiliares.

Ciclos y caminos hamiltonianos

Artículo principal: Ciclo hamiltoniano

Ejemplo de un ciclo hamiltoniano.

Un ciclo es una sucesión de aristas adyacentes, donde no se recorre dos veces la misma arista, y donde se regresa al punto inicial. Un ciclo hamiltoniano tiene además que recorrer todos los vértices exactamente una vez (excepto el vértice del que parte y al cual llega).

Por ejemplo, en un museo grande (al estilo del Louvre), lo idóneo sería recorrer todas las salas una sola vez, esto es buscar un ciclo hamiltoniano en el grafo que representa el museo (los vértices son las salas, y las aristas los corredores o puertas entre ellas).

Se habla también de camino hamiltoniano si no se impone regresar al punto de partida, como en un museo con una única puerta de entrada. Por ejemplo, un caballo puede recorrer todas las casillas de un tablero de ajedrez sin pasar dos veces por la misma: es un camino hamiltoniano. Ejemplo de un ciclo hamiltoniano en el grafo del dodecaedro.

Hoy en día, no se conocen métodos generales para hallar un ciclo hamiltoniano en tiempo polinómico, siendo la búsqueda por fuerza bruta de todos los posibles caminos u otros métodos excesivamente costosos. Existen, sin embargo, métodos para descartar la existencia de ciclos o caminos hamiltonianos en grafos pequeños.

El problema de determinar la existencia de ciclos hamiltonianos, entra en el conjunto de los NP-completos.

Caracterización de grafos

Grafos simples

Un grafo es simple si sólo 1 arista QUE une dos vértices cualesquiera. Esto es equivalente a decir que una arista cualquiera es la única que une dos vértices específicos.

Un grafo que no es simple se denomina Multigráfica o Gráfo múltiple.

Grafos conexos

Un grafo es conexo si cada par de vértices está conectado por un camino; es decir, si para cualquier par de vértices (a, b), existe al menos un camino posible desde a hacia b.

Un grafo es doblemente conexo si cada par de vértices está conectado por al menos dos caminos disjuntos; es decir, es conexo y no existe un vértice tal que al sacarlo el grafo resultante sea disconexo.

Es posible determinar si un grafo es conexo usando un algoritmo Búsqueda en anchura (BFS) o Búsqueda en profundidad (DFS).

En términos matemáticos la propiedad de un grafo de ser (fuertemente) conexo permite establecer con base en él una relación de equivalencia para sus vértices, la cual lleva a una partición de éstos en «componentes (fuertemente) conexas», es decir, porciones del grafo, que son (fuertemente) conexas cuando se consideran como grafos aislados. Esta propiedad es importante para muchas demostraciones en teoría de grafos. GrafoConexo.jpg

Grafos completos

Artículo principal: Grafo completo

Un grafo es completo si existen aristas uniendo todos los pares posibles de vértices. Es decir, todo par de vértices (a, b) debe tener una arista e que los une.

El conjunto de los grafos completos es denominado usualmente \mathbb{K}, siendo \mathbb{K}_n el grafo completo de n vértices.

Un \mathbb{K}_n, es decir, grafo completo de n vértices tiene exactamente \frac{n(n-1)}{2} aristas.

La representación gráfica de los \mathbb{K}_n como los vértices de un polígono regular da cuenta de su peculiar estructura.

Grafos bipartitos

Artículo principal: Grafo bipartito

Un grafo G es bipartito si puede expresarse como G = \{V_1 \cup V_2, A\} (es decir, sus vértices son la unión de dos grupos de vértices), bajo las siguientes condiciones:

  • V1 y V2 son disjuntos y no vacíos.
  • Cada arista de A une un vértice de V1 con uno de V2.
  • No existen aristas uniendo dos elementos de V1; análogamente para V2.

Bajo estas condiciones, el grafo se considera bipartito, y puede describirse informalmente como el grafo que une o relaciona dos conjuntos de elementos diferentes, como aquellos resultantes de los ejercicios y puzzles en los que debe unirse un elemento de la columna A con un elemento de la columna B.

Operaciones en Grafos

Subdivisión elemental de una arista

u \stackrel{e}{\rightarrow} v se convierte en u \stackrel{e'}{\rightarrow} w \stackrel{e''}{\rightarrow} v

Se reemplaza la arista e=\left\{u,v\right\} por dos aristas e'=\left\{u,w\right\} e''=\left\{w,v\right\} y un vértice w.

Después de realizar esta operación, el grafo queda con un vértice y una arista más.

Eliminación débil de un vértice

Si v \in V_G y g(v) = 2 (Sea v un vértice del grafo y de grado dos) eliminarlo débilmente significa reemplazarlo por una arista que une los vértices adyacentes a v.

u \stackrel{e'}{\rightarrow} w \stackrel{e''}{\rightarrow} v se convierte en u \stackrel{e}{\rightarrow} v

Entonces e‘ y e» desaparecen y aparece e=\left\{u,v\right\}

Homeomorfismo de grafos

Artículo principal: Homeomorfismo de grafos

Dos grafos G1 y G2 son homeomorfos si ambos pueden obtenerse a partir del mismo grafo con una sucesión de subdivisiones elementales de aristas.

Árboles

Artículo principal: Árbol (teoría de grafos)

Ejemplo de árbol.

Un grafo que no tiene ciclos y que conecta a todos los puntos, se llama un árbol. En un grafo con n vértices, los árboles tienen exactamente n – 1 aristas, y hay nn-2 árboles posibles. Su importancia radica en que los árboles son grafos que conectan todos los vértices utilizando el menor número posible de aristas. Un importante campo de aplicación de su estudio se encuentra en el análisis filogenético, el de la filiación de entidades que derivan unas de otras en un proceso evolutivo, que se aplica sobre todo a la averiguación del parentesco entre especies; aunque se ha usado también, por ejemplo, en el estudio del parentesco entre lenguas.

Grafos ponderados o etiquetados

En muchos casos, es preciso atribuir a cada arista un número específico, llamado valuación, ponderación o coste según el contexto, y se obtiene así un grafo valuado.
Formalmente, es un grafo con una función v: A → R+.

Por ejemplo, un representante comercial tiene que visitar n ciudades conectadas entre sí por carreteras; su interés previsible será minimizar la distancia recorrida (o el tiempo, si se pueden prever atascos). El grafo correspondiente tendrá como vértices las ciudades, como aristas las carreteras y la valuación será la distancia entre ellas.
Y, de momento, no se conocen métodos generales para hallar un ciclo de valuación mínima, pero sí para los caminos desde a hasta b, sin más condición.

[editar] Teorema de los cuatro colores

Artículo principal: Teorema de los cuatro colores

En 1852 Francis Guthrie planteó el problema de los cuatro colores.

Otro problema famoso relativo a los grafos: ¿Cuántos colores son necesarios para dibujar un mapa político, con la condición obvia que dos países adyacentes no puedan tener el mismo color? Se supone que los países son de un solo pedazo, y que el mundo es esférico o plano. En un mundo en forma de toroide; el teorema siguiente no es válido:

Cuatro colores son siempre suficientes para colorear un mapa.

El mapa siguiente muestra que tres colores no bastan: Si se empieza por el país central a y se esfuerza uno en utilizar el menor número de colores, entonces en la corona alrededor de a alternan dos colores. Llegando al país h se tiene que introducir un cuarto color. Lo mismo sucede en i si se emplea el mismo método.

La forma precisa de cada país no importa; lo único relevante es saber qué país toca a qué otro. Estos datos están incluidos en el grafo donde los vértices son los países y las aristas conectan los que justamente son adyacentes. Entonces la cuestión equivale a atribuir a cada vértice un color distinto del de sus vecinos.

Hemos visto que tres colores no son suficientes, y demostrar que con cinco siempre se llega, es bastante fácil. Pero el teorema de los cuatro colores no es nada obvio. Prueba de ello es que se han tenido que emplear ordenadores para acabar la demostración (se ha hecho un programa que permitió verificar una multitud de casos, lo que ahorró muchísimo tiempo a los matemáticos). Fue la primera vez que la comunidad matemática aceptó una demostración asistida por ordenador, lo que ha creado una fuerte polémica dentro de la comunidad matemática, llegando en algunos casos a plantearse la cuestión de que esta demostración y su aceptación es uno de los momentos que han generado una de las más terribles crisis en el mundo matemático.

Coloración de grafos

Artículo principal: Coloración de grafos

Colores en los vértices.

Definición: Si G=(V, E) es un grafo no dirigido, una coloración propia de G, ocurre cuando coloreamos los vértices de G de modo que si {a, b} es una arista en G entonces a y b tienen diferentes colores. (Por lo tanto, los vértices adyacentes tienen colores diferentes). El número mínimo de colores necesarios para una coloración propia de G es el número cromático de G y se escribe como C (G). Sea G un grafo no dirigido sea λ el número de colores disponibles para la coloración propia de los vértices de G. Nuestro objetivo es encontrar una función polinomial P (G,λ), en la variable λ, llamada polinomio cromático de G , que nos indique el número de coloraciones propias diferentes de los vértices de G, usando un máximo de λ colores.

Descomposición de polinomios cromáticos. Si G=(V, E) es un grafo conexo y e pertenece a Ε , entonces: P (G,λ)=P (G+e,λ)+P (G/e,λ), donde G/e es el grafo se obtene por contracción de aristas.

Para cualquier grafo G, el término constante en P (G,λ) es 0

Sea G=(V, E) con |E|>0 entonces, la suma de los coeficientes de P (G,λ) es 0.

Sea G=(V, E), con a, b pertenecientes al conjunto de vértices V pero {a, b}=e, no perteneciente a al conjunto de aristas E. Escribimos G+e para el grafo que se obtiene de G al añadir la arista e={a, b}. Al identificar los vértices a y b en G, obtenemos el subgrafo G++e de G.

Grafos planos

Artículo principal: Grafo plano

Un grafo es plano si se puede dibujar sin cruces de aristas. El problema de las tres casas y los tres pozos tiene solución sobre el toro, pero no en el plano.

Cuando un grafo o multigrafo se puede dibujar en un plano sin que dos segmentos se corten, se dice que es plano.

Un juego muy conocido es el siguiente: Se dibujan tres casas y tres pozos. Todos los vecinos de las casas tienen el derecho de utilizar los tres pozos. Como no se llevan bien en absoluto, no quieren cruzarse jamás. ¿Es posible trazar los nueve caminos que juntan las tres casas con los tres pozos sin que haya cruces?

Cualquier disposición de las casas, los pozos y los caminos implica la presencia de al menos un cruce.

Sea Kn el grafo completo con n vértices, Kn, p es el grafo bipartito de n y p vértices.

El juego anterior equivale a descubrir si el grafo bipartito completo K3,3 es plano, es decir, si se puede dibujar en un plano sin que haya cruces, siendo la respuesta que no. En general, puede determinarse que un grafo no es plano, si en su diseño puede encontrase una estructura análoga (conocida como menor) a K5 o a K3,3.

Establecer qué grafos son planos no es obvio, y es un problema que tiene que ver con topología.

Diámetro

En la figura se nota que K4 es plano (desviando la arista ab al exterior del cuadrado), que K5 no lo es, y que K3,2 lo es también (desvíos en gris).

En un grafo, la distancia entre dos vértices es el menor número de aristas de un recorrido entre ellos. El diámetro, en una figura como en un grafo, es la mayor distancia entre dos puntos de la misma.

El diámetro de los Kn es 1, y el de los Kn,p es 2. Un diámetro infinito puede significar que el grafo tiene una infinidad de vértices o simplemente que no es conexo. También se puede considerar el diámetro promedio, como el promedio de las distancias entre dos vértices.

El mundo de Internet ha puesto de moda esa idea del diámetro: Si descartamos los sitios que no tienen enlaces, y escogemos dos páginas web al azar: ¿En cuántos clics se puede pasar de la primera a la segunda? El resultado es el diámetro de la Red, vista como un grafo cuyos vértices son los sitios, y cuyas aristas son lógicamente los enlaces.

En el mundo real hay una analogía: tomando al azar dos seres humanos del mundo, ¿En cuántos saltos se puede pasar de uno a otro, con la condición de sólo saltar de una persona a otra cuando ellas se conocen personalmente? Con esta definición, se estima que el diámetro de la humanidad es de… ¡ocho solamente!

Este concepto refleja mejor la complejidad de una red que el número de sus elementos.

 

 

Referencias

http://es.wikipedia.org/wiki/%C3%81rbol_binario

http://es.wikipedia.org/wiki/Teor%C3%ADa_de_grafos

http://es.wikipedia.org/wiki/Teor%C3%ADa_de_grafos#Grafo

Árboles

Arbol=Estructura datos jerarquica compuesta nodos dinamicos mismo tipo nodo_raiz=de el derivan todos

nodo_terminal=no tienen descendientes altura=niveles tipos=bin,ter…max 2,3 desc

orden=preorden,inorden,postorden struct t_nodo { int num; struct t_nodo *izq,*der; };

inserta(struct t_nodo **,int)

{ struct t_nodo *paux,*parbaux; int encontrado=0;

paux=(struct t_nodo *)malloc(sizeof(struct t_nodo));

paux->num=valor; paux->izq=paux->der=NULL; if(*parbol==NULL) *parbol=paux;

else { parbaux=*parbol; while(!encontrado)

{ if(parbaux->num<valor) if(parbaux->der==NULL)

{ parbaux->der=paux; encontrado=1;

} else parbaux=parbaux->der; else if(parbaux->izq==NULL)

{ parbaux->izq=paux; encontrado=1; } else parbaux=parbaux->izq; } } } lista(struct t_nodo *) { if(arbol->izq!=NULL) lista(arbol->izq); if(arbol==NULL) printf(«%d»,arbol->num); if(arbol->der!=NULL) lista(arbol->der); } int existe(struct t_nodo *arbol,int valor) { int existe_izq,existe_der; if(arbol!=NULL) if(arbol->num==valor) return 1; else { existe_izq=existe(arbol->izq,valor); existe_der=existe(arbol->der,valor); return existe_izq+existe_der; } else return 0; } borra(struct t_nodo **) { if(*parbol!=NULL) { borra(&((*parbol)->izq)); borra(&((*parbol)->der)); free(*parbol); *parbol=NULL; } }

Identificar 🙂

Una estructura básica de un nodo para crear listas de datos seria:

struct nodo {
   int dato;
   struct nodo *otronodo;
};

El campo «otronodo» puede apuntar a un objeto del tipo nodo. De este modo, cada nodo puede usarse como un ladrillo para construir listas de datos, y cada uno mantendrá ciertas relaciones con otros nodos.

Para acceder a un nodo de la estructura sólo necesitaremos un puntero a un nodo.

Durante el presente curso usaremos gráficos para mostrar la estructura de las estructuras de datos dinámicas. El nodo anterior se representará asi:

gráfico de nodo
Nodo

Las estructuras dinámicas son una implementación de TDAs o TADs (Tipos Abstractos de Datos). En estos tipos el interés se centra más en la estructura de los datos que en el tipo concreto de información que almacenan.

Dependiendo del número de punteros y de las relaciones entre nodos, podemos distinguir varios tipos de estructuras dinámicas. Enumeraremos ahora sólo de los tipos básicos:

  • Listas abiertas: cada elemento sólo dispone de un puntero, que apuntará al siguiente elemento de la lista o valdrá NULL si es el último elemento.
  • Pilas: son un tipo especial de lista, conocidas como listas LIFO (Last In, First Out: el último en entrar es el primero en salir). Los elementos se «amontonan» o apilan, de modo que sólo el elemento que está encima de la pila puede ser leído, y sólo pueden añadirse elementos encima de la pila.
  • Colas: otro tipo de listas, conocidas como listas FIFO (First In, First Out: El primero en entrar es el primero en salir). Los elementos se almacenan en fila, pero sólo pueden añadirse por un extremo y leerse por el otro.
  • Listas circulares: o listas cerradas, son parecidas a las listas abiertas, pero el último elemento apunta al primero. De hecho, en las listas circulares no puede hablarse de «primero» ni de «último». Cualquier nodo puede ser el nodo de entrada y salida.
  • Listas doblemente enlazadas: cada elemento dispone de dos punteros, uno a punta al siguiente elemento y el otro al elemento anterior. Al contrario que las listas abiertas anteriores, estas listas pueden recorrerse en los dos sentidos.
  • Arboles: cada elemento dispone de dos o más punteros, pero las referencias nunca son a elementos anteriores, de modo que la estructura se ramifica y crece igual que un árbol.
  • Arboles binarios: son árboles donde cada nodo sólo puede apuntar a dos nodos.
  • Arboles binarios de búsqueda (ABB): son árboles binarios ordenados. Desde cada nodo todos los nodos de una rama serán mayores, según la norma que se haya seguido para ordenar el árbol, y los de la otra rama serán menores.
  • Arboles AVL: son también árboles de búsqueda, pero su estructura está más optimizada para reducir los tiempos de búsqueda.
  • Arboles B: son estructuras más complejas, aunque también se trata de árboles de búsqueda, están mucho más optimizados que los anteriores.
  • Tablas HASH: son estructuras auxiliares para ordenar listas.
  • Grafos: es el siguiente nivel de complejidad, podemos considerar estas estructuras como árboles no jerarquizados.
  • Diccionarios.

Arboles**

Un árbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.

También se suele dar una definición recursiva: un árbol es una estructura en compuesta por un dato y varios árboles.

Esto son definiciones simples. Pero las características que implican no lo son tanto.

Árbol
Árbol

Definiremos varios conceptos. En relación con otros nodos:

  • Nodo hijo: cualquiera de los nodos apuntados por uno de los nodos del árbol. En el ejemplo, ‘L’ y ‘M’ son hijos de ‘G’.
  • Nodo padre: nodo que contiene un puntero al nodo actual. En el ejemplo, el nodo ‘A’ es padre de ‘B’, ‘C’ y ‘D’.

Los árboles con los que trabajaremos tienen otra característica importante: cada nodo sólo puede ser apuntado por otro nodo, es decir, cada nodo sólo tendrá un padre. Esto hace que estos árboles estén fuertemente jerarquizados, y es lo que en realidad les da la apariencia de árboles.

En cuanto a la posición dentro del árbol:

  • Nodo raíz: nodo que no tiene padre. Este es el nodo que usaremos para referirnos al árbol. En el ejemplo, ese nodo es el ‘A’.
  • Nodo hoja: nodo que no tiene hijos. En el ejemplo hay varios: ‘F’, ‘H’, ‘I’, ‘K’, ‘L’, ‘M’, ‘N’ y ‘O’.
  • Nodo rama: aunque esta definición apenas la usaremos, estos son los nodos que no pertenecen a ninguna de las dos categorías anteriores. En el ejemplo: ‘B’, ‘C’, ‘D’, ‘E’, ‘G’ y ‘J’.

Otra característica que normalmente tendrán nuestros árboles es que todos los nodos contengan el mismo número de punteros, es decir, usaremos la misma estructura para todos los nodos del árbol. Esto hace que la estructura sea más sencilla, y por lo tanto también los programas para trabajar con ellos.

Tampoco es necesario que todos los nodos hijos de un nodo concreto existan. Es decir, que pueden usarse todos, algunos o ninguno de los punteros de cada nodo.

Un árbol en el que en cada nodo o bien todos o ninguno de los hijos existe, se llama árbol completo.

En una cosa, los árboles se parecen al resto de las estructuras que hemos visto: dado un nodo cualquiera de la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo cualquiera puede ser considerado como la raíz de un árbol completo.

Existen otros conceptos que definen las características del árbol, en relación a su tamaño:

  • Orden: es el número potencial de hijos que puede tener cada elemento de árbol. De este modo, diremos que un árbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede apuntar a tres será de orden tres, etc.
  • Grado: el número de hijos que tiene el elemento con más hijos dentro del árbol. En el árbol del ejemplo, el grado es tres, ya que tanto ‘A’ como ‘D’ tienen tres hijos, y no existen elementos con más de tres hijos.
  • Nivel: se define para cada elemento del árbol como la distancia a la raíz, medida en nodos. El nivel de la raíz es cero y el de sus hijos uno. Así sucesivamente. En el ejemplo, el nodo ‘D’ tiene nivel 1, el nodo ‘G’ tiene nivel 2, y el nodo ‘N’, nivel 3.
  • Altura: la altura de un árbol se define como el nivel del nodo de mayor nivel. Como cada nodo de un árbol puede considerarse a su vez como la raíz de un árbol, también podemos hablar de altura de ramas. El árbol del ejemplo tiene altura 3, la rama ‘B’ tiene altura 2, la rama ‘G’ tiene altura 1, la ‘H’ cero, etc.

Los árboles de orden dos son bastante especiales, de hecho les dedicaremos varios capítulos. Estos árboles se conocen también como árboles binarios.

Frecuentemente, aunque tampoco es estrictamente necesario, para hacer más fácil moverse a través del árbol, añadiremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar en dirección a la raíz, y no sólo hacia las hojas.

Es importante conservar siempre el nodo raíz ya que es el nodo a partir del cual se desarrolla el árbol, si perdemos este nodo, perderemos el acceso a todo el árbol.

El nodo típico de un árbol difiere de los nodos que hemos visto hasta ahora para listas, aunque sólo en el número de nodos. Veamos un ejemplo de nodo para crear árboles de orden tres:

struct nodo {
   int dato;
   struct nodo *rama1;
   struct nodo *rama2;
   struct nodo *rama3;
};

O generalizando más:

#define ORDEN 5

struct nodo {
   int dato;
   struct nodo *rama[ORDEN];
};

**Declaracion de tipos para manejar arboles en C

Para C, y basándonos en la declaración de nodo que hemos visto más arriba, trabajaremos con los siguientes tipos:

typedef struct _nodo {
   int dato;
   struct _nodo *rama[ORDEN];
} tipoNodo;

typedef tipoNodo *pNodo;
typedef tipoNodo *Arbol;

Al igual que hicimos con las listas que hemos visto hasta ahora, declaramos un tipo tipoNodo para declarar nodos, y un tipo pNodo para es el tipo para declarar punteros a un nodo.

Arbol es el tipo para declarar árboles de orden ORDEN.

Árbol
Árbol

El movimiento a través de árboles, salvo que implementemos punteros al nodo padre, será siempre partiendo del nodo raíz hacia un nodo hoja. Cada vez que lleguemos a un nuevo nodo podremos optar por cualquiera de los nodos a los que apunta para avanzar al siguiente nodo.

En general, intentaremos que exista algún significado asociado a cada uno de los punteros dentro de cada nodo, los árboles que estamos viendo son abstractos, pero las aplicaciones no tienen por qué serlo. Un ejemplo de estructura en árbol es el sistema de directorios y ficheros de un sistema operativo. Aunque en este caso se trata de árboles con nodos de dos tipos, nodos directotio y nodos fichero, podríamos considerar que los nodos hoja son ficheros y los nodos rama son directorios.

Otro ejemplo podría ser la tabla de contenido de un libro, por ejemplo de este mismo curso, dividido en capítulos, y cada uno de ellos en subcapítulos. Aunque el libro sea algo lineal, como una lista, en el que cada capítulo sigue al anterior, también es posible acceder a cualquier punto de él a través de la tabla de contenido.

También se suelen organizar en forma de árbol los organigramas de mando en empresas o en el ejército, y los árboles genealógicos.

Salvo que trabajemos con árboles especiales, como los que veremos más adelante, las inserciones serán siempre en punteros de nodos hoja o en punteros libres de nodos rama. Con estas estructuras no es tan fácil generalizar, ya que existen muchas variedades de árboles.

De nuevo tenemos casi el mismo repertorio de operaciones de las que disponíamos con las listas:

  • Añadir o insertar elementos.
  • Buscar o localizar elementos.
  • Borrar elementos.
  • Moverse a través del árbol.
  • Recorrer el árbol completo.

Los algoritmos de inserción y borrado dependen en gran medida del tipo de árbol que estemos implementando, de modo que por ahora los pasaremos por alto y nos centraremos más en el modo de recorrer árboles.

Recorridos por árboles

El modo evidente de moverse a través de las ramas de un árbol es siguiendo los punteros, del mismo modo en que nos movíamos a través de las listas.

Esos recorridos dependen en gran medida del tipo y propósito del árbol, pero hay ciertos recorridos que usaremos frecuentemente. Se trata de aquellos recorridos que incluyen todo el árbol.

Hay tres formas de recorrer un árbol completo, y las tres se suelen implementar mediante recursividad. En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por una.

Supongamos que tenemos un árbol de orden tres, y queremos recorrerlo por completo.

Partiremos del nodo raíz:

RecorrerArbol(raiz);

La función RecorrerArbol, aplicando recursividad, será tan sencilla como invocar de nuevo a la función RecorrerArbol para cada una de las ramas:

void RecorrerArbol(Arbol a) {
   if(a == NULL) return;
   RecorrerArbol(a->rama[0]);
   RecorrerArbol(a->rama[1]);
   RecorrerArbol(a->rama[2]);
}

Lo que diferencia los distintos métodos de recorrer el árbol no es el sistema de hacerlo, sino el momento que elegimos para procesar el valor de cada nodo con relación a los recorridos de cada una de las ramas.

Árbol

Los tres tipos son:

Pre-orden

En este tipo de recorrido, el valor del nodo se procesa antes de recorrer las ramas:

void PreOrden(Arbol a) {
   if(a == NULL) return;
   Procesar(dato);
   RecorrerArbol(a->rama[0]);
   RecorrerArbol(a->rama[1]);
   RecorrerArbol(a->rama[2]);
}

Si seguimos el árbol del ejemplo en pre-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

A B E K F C G L M D H I J N O

In-orden

En este tipo de recorrido, el valor del nodo se procesa después de recorrer la primera rama y antes de recorrer la última. Esto tiene más sentido en el caso de árboles binarios, y también cuando existen ORDEN-1 datos, en cuyo caso procesaremos cada dato entre el recorrido de cada dos ramas (este es el caso de los árboles-b):

void InOrden(Arbol a) {
   if(a == NULL) return;
   RecorrerArbol(a->rama[0]);
   Procesar(dato);
   RecorrerArbol(a->rama[1]);
   RecorrerArbol(a->rama[2]);
}

Si seguimos el árbol del ejemplo en in-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

K E B F A L G M C H D I N J O

Post-orden

En este tipo de recorrido, el valor del nodo se procesa después de recorrer todas las ramas:

void PostOrden(Arbol a) {
   if(a == NULL) return;
   RecorrerArbol(a->rama[0]);
   RecorrerArbol(a->rama[1]);
   RecorrerArbol(a->rama[2]);
   Procesar(dato);
}

Si seguimos el árbol del ejemplo en post-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

K E F B L M G C H I N O J D A

Eliminar nodos en un árbol

El proceso general es muy sencillo en este caso, pero con una importante limitación, sólo podemos borrar nodos hoja:

El proceso sería el siguiente:

  1. Buscar el nodo padre del que queremos eliminar.
  2. Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.
  3. Liberar el nodo.
  4. padre->nodo[i] = NULL;.

Cuando el nodo a borrar no sea un nodo hoja, diremos que hacemos una «poda», y en ese caso eliminaremos el árbol cuya raíz es el nodo a borrar. Se trata de un procedimiento recursivo, aplicamos el recorrido PostOrden, y el proceso será borrar el nodo.

El procedimiento es similar al de borrado de un nodo:

  1. Buscar el nodo padre del que queremos eliminar.
  2. Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.
  3. Podar el árbol cuyo padre es nodo.
  4. padre->nodo[i] = NULL;.

En el árbol del ejemplo, para podar la rama ‘B’, recorreremos el subárbol ‘B’ en postorden, eliminando cada nodo cuando se procese, de este modo no perdemos los punteros a las ramas apuntadas por cada nodo, ya que esas ramas se borrarán antes de eliminar el nodo.

De modo que el orden en que se borrarán los nodos será:

K E F y B

árboles ordenados

A partir del siguiente capítulo sólo hablaremos de árboles ordenados, ya que son los que tienen más interés desde el punto de vista de TAD, y los que tienen más aplicaciones genéricas.

Un árbol ordenado, en general, es aquel a partir del cual se puede obtener una secuencia ordenada siguiendo uno de los recorridos posibles del árbol: inorden, preorden o postorden.

En estos árboles es importante que la secuencia se mantenga ordenada aunque se añadan o se eliminen nodos.

Existen varios tipos de árboles ordenados, que veremos a continuación:

  • árboles binarios de búsqueda (ABB): son árboles de orden 2 que mantienen una secuencia ordenada si se recorren en inorden.
  • árboles AVL: son árboles binarios de búsqueda equilibrados, es decir, los niveles de cada rama para cualquier nodo no difieren en más de 1.
  • árboles perfectamente equilibrados: son árboles binarios de búsqueda en los que el número de nodos de cada rama para cualquier nodo no difieren en más de 1. Son por lo tanto árboles AVL también.
  • árboles 2-3: son árboles de orden 3, que contienen dos claves en cada nodo y que están también equilibrados. También generan secuencias ordenadas al recorrerlos en inorden.
  • árboles-B: caso general de árboles 2-3, que para un orden M, contienen M-1 claves.

Definición

Se trata de árboles de orden 2 en los que se cumple que para cada nodo, el valor de la clave de la raíz del subárbol izquierdo es menor que el valor de la clave del nodo y que el valor de la clave raíz del subárbol derecho es mayor que el valor de la clave del nodo.

Árbol binario de búsqueda

Operaciones en ABB

El repertorio de operaciones que se pueden realizar sobre un ABB es parecido al que realizábamos sobre otras estructuras de datos, más alguna otra propia de árboles:

  • Buscar un elemento.
  • Insertar un elemento.
  • Borrar un elemento.
  • Movimientos a través del árbol:
    • Izquierda.
    • Derecha.
    • Raiz.
  • Información:
    • Comprobar si un árbol está vacío.
    • Calcular el número de nodos.
    • Comprobar si el nodo es hoja.
    • Calcular la altura de un nodo.
    • Calcular la altura de un árbol.

Buscar un elemento

Partiendo siempre del nodo raíz, el modo de buscar un elemento se define de forma recursiva.

  • Si el árbol está vacío, terminamos la búsqueda: el elemento no está en el árbol.
  • Si el valor del nodo raíz es igual que el del elemento que buscamos, terminamos la búsqueda con éxito.
  • Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo.
  • Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho.

El valor de retorno de una función de búsqueda en un ABB puede ser un puntero al nodo encontrado, o NULL, si no se ha encontrado.

Insertar un elemento

Para insertar un elemento nos basamos en el algoritmo de búsqueda. Si el elemento está en el árbol no lo insertaremos. Si no lo está, lo insertaremos a continuación del último nodo visitado.

Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raíz actual. El valor inicial para ese puntero es NULL.

  • Padre = NULL
  • nodo = Raiz
  • Bucle: mientras actual no sea un árbol vacío o hasta que se encuentre el elemento.
    • Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo: Padre=nodo, nodo=nodo->izquierdo.
    • Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho: Padre=nodo, nodo=nodo->derecho.
  • Si nodo no es NULL, el elemento está en el árbol, por lo tanto salimos.
  • Si Padre es NULL, el árbol estaba vacío, por lo tanto, el nuevo árbol sólo contendrá el nuevo elemento, que será la raíz del árbol.
  • Si el elemento es menor que el Padre, entonces insertamos el nuevo elemento como un nuevo árbol izquierdo de Padre.
  • Si el elemento es mayor que el Padre, entonces insertamos el nuevo elemento como un nuevo árbol derecho de Padre.

Este modo de actuar asegura que el árbol sigue siendo ABB.

Borrar un elemento

Para borrar un elemento también nos basamos en el algoritmo de búsqueda. Si el elemento no está en el árbol no lo podremos borrar. Si está, hay dos casos posibles:

  1. Se trata de un nodo hoja: en ese caso lo borraremos directamente.
  2. Se trata de un nodo rama: en ese caso no podemos eliminarlo, puesto que perderíamos todos los elementos del árbol de que el nodo actual es padre. En su lugar buscamos el nodo más a la izquierda del subárbol derecho, o el más a la derecha del subárbol izquierdo e intercambiamos sus valores. A continuación eliminamos el nodo hoja.

Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raíz actual. El valor inicial para ese puntero es NULL.

  • Padre = NULL
  • Si el árbol está vacío: el elemento no está en el árbol, por lo tanto salimos sin eliminar ningún elemento.
  • (1) Si el valor del nodo raíz es igual que el del elemento que buscamos, estamos ante uno de los siguientes casos:
    • El nodo raíz es un nodo hoja:
      • Si ‘Padre’ es NULL, el nodo raíz es el único del árbol, por lo tanto el puntero al árbol debe ser NULL.
      • Si raíz es la rama derecha de ‘Padre’, hacemos que esa rama apunte a NULL.
      • Si raíz es la rama izquierda de ‘Padre’, hacemos que esa rama apunte a NULL.
      • Eliminamos el nodo, y salimos.
    • El nodo no es un nodo hoja:
      • Buscamos el ‘nodo’ más a la izquierda del árbol derecho de raíz o el más a la derecha del árbol izquierdo. Hay que tener en cuenta que puede que sólo exista uno de esos árboles. Al mismo tiempo, actualizamos ‘Padre’ para que apunte al padre de ‘nodo’.
      • Intercambiamos los elementos de los nodos raíz y ‘nodo’.
      • Borramos el nodo ‘nodo’. Esto significa volver a (1), ya que puede suceder que ‘nodo’ no sea un nodo hoja. (Ver ejemplo 3)
  • Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo.
  • Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho.

Ejemplo 1: Borrar un nodo hoja

En el árbol de ejemplo, borrar el nodo 3.

  1. Localizamos el nodo a borrar, al tiempo que mantenemos un puntero a ‘Padre’.
  2. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULL.
  3. Borramos el ‘nodo’.
Borrar un nodo hoja

Ejemplo 2: Borrar un nodo rama con intercambio de un nodo hoja.

En el árbol de ejemplo, borrar el nodo 4.

  1. Localizamos el nodo a borrar (‘raíz’).
  2. Buscamos el nodo más a la derecha del árbol izquierdo de ‘raíz’, en este caso el 3, al tiempo que mantenemos un puntero a ‘Padre’ a ‘nodo’.
  3. Intercambiamos los elementos 3 y 4.
  4. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULL.
  5. Borramos el ‘nodo’.
Borrar con intercambio de nodo hoja

Ejemplo 3: Borrar un nodo rama con intercambio de un nodo rama.

Para este ejemplo usaremos otro árbol. En éste borraremos el elemento 6.

Árbol binario de búsqueda
    1. Localizamos el nodo a borrar (‘raíz’).
    2. Buscamos el nodo más a la izquierda del árbol derecho de ‘raíz’, en este caso el 12, ya que el árbol derecho no tiene nodos a su izquierda, si optamos por la rama izquierda, estaremos en un caso análogo. Al mismo tiempo que mantenemos un puntero a ‘Padre’ a ‘nodo’.
    3. Intercambiamos los elementos 6 y 12.
    4. Ahora tenemos que repetir el bucle para el nodo 6 de nuevo, ya que no podemos eliminarlo.
Borrar con intercambio de nodo rama (1)
    1. Localizamos de nuevo el nodo a borrar (‘raíz’).
    2. Buscamos el nodo más a la izquierda del árbol derecho de ‘raíz’, en este caso el 16, al mismo tiempo que mantenemos un puntero a ‘Padre’ a ‘nodo’.
    3. Intercambiamos los elementos 6 y 16.
    4. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULL.
    5. Borramos el ‘nodo’.
Borrar con intercambio de nodo rama (2)

Este modo de actuar asegura que el árbol sigue siendo ABB.

Movimientos a través del árbol

No hay mucho que contar. Nuestra estructura se referenciará siempre mediante un puntero al nodo Raiz, este puntero no debe perderse nunca.

Para movernos a través del árbol usaremos punteros auxiliares, de modo que desde cualquier puntero los movimientos posibles serán: moverse al nodo raíz de la rama izquierda, moverse al nodo raíz de la rama derecha o moverse al nodo Raiz del árbol.

Información

Hay varios parámetros que podemos calcular o medir dentro de un árbol. Algunos de ellos nos darán idea de lo eficientemente que está organizado o el modo en que funciona.

Comprobar si un árbol está vacío.

Un árbol está vacío si su raíz es NULL.

Calcular el número de nodos.

Tenemos dos opciones para hacer esto, una es llevar siempre la cuenta de nodos en el árbol al mismo tiempo que se añaden o eliminan elementos. La otra es, sencillamente, contarlos.

Para contar los nodos podemos recurrir a cualquiera de los tres modos de recorrer el árbol: inorden, preorden o postorden, como acción sencillamente incrementamos el contador.

Comprobar si el nodo es hoja.

Esto es muy sencillo, basta con comprobar si tanto el árbol izquierdo como el derecho están vacíos. Si ambos lo están, se trata de un nodo hoja.

Calcular la altura de un nodo.

No hay un modo directo de hacer esto, ya que no nos es posible recorrer el árbol en la dirección de la raíz. De modo que tendremos que recurrir a otra técnica para calcular la altura.

Lo que haremos es buscar el elemento del nodo de que queremos averiguar la altura. Cada vez que avancemos un nodo incrementamos la variable que contendrá la altura del nodo.

  • Empezamos con el nodo raíz apuntando a Raiz, y la ‘Altura’ igual a cero.
  • Si el valor del nodo raíz es igual que el del elemento que buscamos, terminamos la búsqueda y el valor de la altura es ‘Altura’.
  • Incrementamos ‘Altura’.
  • Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo.
  • Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho.

Calcular la altura de un árbol.

La altura del árbol es la altura del nodo de mayor altura. Para buscar este valor tendremos que recorrer todo el árbol, de nuevo es indiferente el tipo de recorrido que hagamos, cada vez que cambiemos de nivel incrementamos la variable que contiene la altura del nodo actual, cuando lleguemos a un nodo hoja compararemos su altura con la variable que contiene la altura del árbol si es mayor, actualizamos la altura del árbol.

  • Iniciamos un recorrido del árbol en postorden, con la variable de altura igual a cero.
  • Cada vez que empecemos a recorrer una nueva rama, incrementamos la altura para ese nodo.
  • Después de procesar las dos ramas, verificamos si la altura del nodo es mayor que la variable que almacena la altura actual del árbol, si es así, actualizamos esa variable.

árboles degenerados

Los árboles binarios de búsqueda tienen un gran inconveniente. Por ejemplo, supongamos que creamos un ABB a partir de una lista de valores ordenada:

2, 4, 5, 8, 9, 12

Difícilmente podremos llamar a la estructura resultante un árbol:

Árbol degenerado
Árbol binario de búsqueda degenerado

Esto es lo que llamamos un árbol binario de búsqueda degenerado, y en el siguiente capítulo veremos una nueva estructura, el árbol AVL, que resuelve este problema, generando árboles de búsqueda equilibrados.

Referencias

http://c.conclase.net/edd/?cap=006#inicio

Pilas y Colas

Una pila (stack en inglés) es una lista ordinal o estructura de datos en la que el modo de acceso a sus elementos es de tipo LIFO (del inglés Last In First Out, último en entrar, primero en salir) que permite almacenar y recuperar datos. Esta estructura se aplica en multitud de ocasiones en el área de informática debido a su simplicidad y ordenación implícita de la propia estructura.

en pocas palabras…

Pila:

Estructura lineal

Acceso de inserción y eliminación  por un solo extremo

Manipulacion:

Meter por un extremo: push(x)
Sacar por el mismo extremo: pop()Representación gráfica de una pila

Para el manejo de los datos se cuenta con dos operaciones básicas: apilar (push), que coloca un objeto en la pila, y su operación inversa, retirar (o desapilar, pop), que retira el último elemento apilado.

En cada momento sólo se tiene acceso a la parte superior de la pila, es decir, al último objeto apilado (denominado TOS, Top of Stack en inglés). La operación retirar permite la obtención de este elemento, que es retirado de la pila permitiendo el acceso al siguiente (apilado con anterioridad), que pasa a ser el nuevo TOS.

Por analogía con objetos cotidianos, una operación apilar equivaldría a colocar un plato sobre una pila de platos, y una operación retirar a retirarlo.

Las pilas suelen emplearse en los siguientes contextos:

**Pila como tipo abstracto de datos  😀

A modo de resumen tipo de datos, la pila es un contenedor de nodos y tiene dos operaciones básicas: push (o apilar) y pop (o desapilar). ‘Push’ añade un nodo a la parte superior de la pila, dejando por debajo el resto de los nodos. ‘Pop’ elimina y devuelve el actual nodo superior de la pila. Una metáfora que se utiliza con frecuencia es la idea de una pila de platos en una cafetería con muelle de pila. En esa serie, sólo la primera placa es visible y accesible para el usuario, todas las demás placas permanecen ocultas. Como se añaden las nuevas placas, cada nueva placa se convierte en la parte superior de la pila, escondidos debajo de cada plato, empujando a la pila de placas. A medida que la placa superior se elimina de la pila, la segunda placa se convierte en la parte superior de la pila. Dos principios importantes son ilustrados por esta metáfora: En primer lugar la última salida es un principio, la segunda es que el contenido de la pila está oculto. Sólo la placa de la parte superior es visible, por lo que para ver lo que hay en la tercera placa, el primer y segundo platos tendrán que ser retirados.

* Operaciones :$

Una pila cuenta con 2 operaciones imprescindibles: apilar y desapilar, a las que en las implementaciones modernas de las pilas se suelen añadir más de uso habitual.

Crear: se crea la pila vacía.

Apilar: se añade un elemento a la pila.(push)

Desapilar: se elimina el elemento frontal de la pila.(pop)

Cima: devuelve el elemento que esta en la cima de la pila. (top o peek)

Vacía: devuelve cierto si la pila está vacía o falso en caso contrario.

**Implementación 😮

Un requisito típico de almacenamiento de una pila de n elementos es O(n). El requisito típico de tiempo de O(1) las operaciones también son fáciles de satisfacer con un array o con listas enlazadas simples.

La biblioteca de plantillas de C++ estándar proporciona una «pila» clase templated que se limita a sólo apilar/desapilar operaciones. Java contiene una biblioteca de la clase Pila que es una especialización de Vector. Esto podría ser considerado como un defecto, porque el diseño heredado get () de Vector método LIFO ignora la limitación de la Pila.

Estos son ejemplos sencillos de una pila con las operaciones descritas anteriormente (pero no hay comprobación de errores).

Un uso muy común de las pilas a nivel de arquitectura hardware es la asignación de memoria.

Una pila típica es un área de la memoria de los computadores con un origen fijo y un tamaño variable. Al principio, el tamaño de la pila es cero. Un puntero de pila, por lo general en forma de un registro de hardware, apunta a la más reciente localización en la pila; cuando la pila tiene un tamaño de cero, el puntero de pila de puntos en el origen de la pila.

Las dos operaciones aplicables a todas las pilas son:

  • Una operación apilar, en el que un elemento de datos se coloca en el lugar apuntado por el puntero de pila, y la dirección en el puntero de pila se ajusta por el tamaño de los datos de partida.
  • Una operación desapilar: un elemento de datos en la ubicación actual apuntado por el puntero de pila es eliminado, y el puntero de pila se ajusta por el tamaño de los datos de partida.

Hay muchas variaciones en el principio básico de las operaciones de pila. Cada pila tiene un lugar fijo en la memoria en la que comienza. Como los datos se añadirán a la pila, el puntero de pila es desplazado para indicar el estado actual de la pila, que se expande lejos del origen (ya sea hacia arriba o hacia abajo, dependiendo de la aplicación concreta).

Por ejemplo, una pila puede comenzar en una posición de la memoria de mil, y ampliar por debajo de las direcciones, en cuyo caso, los nuevos datos se almacenan en lugares que van por debajo de 1000, y el puntero de pila se decrementa cada vez que un nuevo elemento se agrega. Cuando un tema es eliminado de la pila, el puntero de pila se incrementa.

Los punteros de pila pueden apuntar al origen de una pila o de un número limitado de direcciones, ya sea por encima o por debajo del origen (dependiendo de la dirección en que crece la pila), sin embargo el puntero de pila no puede cruzar el origen de la pila. En otras palabras, si el origen de la pila está en la dirección 1000 y la pila crece hacia abajo (hacia las direcciones 999, 998, y así sucesivamente), el puntero de pila nunca debe ser incrementado más allá de 1000 (para 1001, 1002, etc.) Si un desapilar operación en la pila hace que el puntero de pila se deje atrás el origen de la pila, una pila se produce desbordamiento. Si una operación de apilar hace que el puntero de pila incremente o decremente más allá del máximo de la pila, en una pila se produce desbordamiento.

La pila es visualizada ya sea creciente de abajo hacia arriba (como pilas del mundo real), o, con el máximo elemento de la pila en una posición fija, o creciente, de izquierda a derecha, por lo que el máximo elemento se convierte en el máximo a «la derecha». Esta visualización puede ser independiente de la estructura real de la pila en la memoria. Esto significa que rotar a la derecha es mover el primer elemento a la tercera posición, la segunda a la primera y la tercera a la segunda. Aquí hay dos equivalentes visualizaciones de este proceso:

 Manzana                                            Plátano

 Plátano       ==rotar a la derecha==>              Fresa

 Fresa                                              Manzana

 Fresa                                             Manzana

 Plátano       ==rotar a la izquierda==>           Fresa

 Manzana                                           Plátano

Una pila es normalmente representada en los ordenadores por un bloque de celdas de memoria, con los «de abajo» en una ubicación fija, y el puntero de pila de la dirección actual de la «cima» de células de la pila. En la parte superior e inferior se utiliza la terminología con independencia de que la pila crece realmente a la baja de direcciones de memoria o direcciones de memoria hacia mayores.

Apilando un elemento en la pila,se ajusta el puntero de pila por el tamaño de elementos (ya sea decrementar o incrementar, en función de la dirección en que crece la pila en la memoria), que apunta a la próxima celda, y copia el nuevo elemento de la cima en área de la pila. Dependiendo de nuevo sobre la aplicación exacta, al final de una operación de apilar, el puntero de pila puede señalar a la siguiente ubicación no utilizado en la pila, o tal vez apunte al máximo elemento de la pila. Si la pila apunta al máximo elemento de la pila, el puntero de pila se actualizará antes de que un nuevo elemento se apile, si el puntero que apunta a la próxima ubicación disponible en la pila, que se actualizará después de que el máximo elemento se apile en la pila.

Desapilando es simplemente la inversa de apilar. El primer elemento de la pila es eliminado y el puntero de pila se actualiza, en el orden opuesto de la utilizada en la operación de apilar.

Expresión de evaluación y análisis sintáctico

Se calcula empleando la notación polaca inversa utilizando una estructura de pila para los posibles valores. Las expresiones pueden ser representadas en prefijo, infijo, postfijo. La conversión de una forma de la expresión a otra forma necesita de una pila. Muchos compiladores utilizan una pila para analizar la sintaxis de las expresiones, bloques de programa, etc. Antes de traducir el código de bajo nivel. La mayoría de los lenguajes de programación son de contexto libre de los idiomas que les permite ser analizados con máquinas basadas en la pila.

Por ejemplo, el cálculo: ((1 + 2) * 4) + 3, puede ser anotado como en notación postfija con la ventaja de no prevalecer las normas y los paréntesis necesario:

1 2 + 4 * 3 +

La expresión es evaluada de izquierda a derecha utilizando una pila:

  • Apilar cuando se enfrentan a un operando y
  • Desafilar dos operandos y evaluar el valor cuando se enfrentan a una operación.
  • Apilar el resultado.

De la siguiente manera (la Pila se muestra después de que la operación se haya llevado a cabo):

 ENTRADA         OPERACION                 PILA

 1             Apilar operando             1
 2             Apilar operando             1, 2
 +             Añadir                      3
 4             Apilar operando             3, 4
 *             Multiplicar                 12
 3             Apilar operando             12, 3
 +             Añadir                      15

El resultado final, 15, se encuentra en la parte superior de la pila al final del cálculo.

Expresión de evaluación y análisis sintáctico

Se calcula empleando la notación polaca inversa utilizando una estructura de pila para los posibles valores. Las expresiones pueden ser representadas en prefijo, infijo, postfijo. La conversión de una forma de la expresión a otra forma necesita de una pila. Muchos compiladores utilizan una pila para analizar la sintaxis de las expresiones, bloques de programa, etc. Antes de traducir el código de bajo nivel. La mayoría de los lenguajes de programación son de contexto libre de los idiomas que les permite ser analizados con máquinas basadas en la pila.

______________________________________________________________________________________________________________________

Una cola es una estructura de datos, caracterizada por ser una secuencia de elementos en la que la operación de inserción push se realiza por un extremo y la operación de extracción pop por el otro. También se le llama estructura FIFO (del inglés First In First Out), debido a que el primer elemento en entrar será también el primero en salir.

Las colas se utilizan en sistemas informáticos, transportes y operaciones de investigación (entre otros), dónde los objetos, personas o eventos son tomados como datos que se almacenan y se guardan mediante colas para su posterior procesamiento. Este tipo de estructura de datos abstracta se implementa en lenguajes orientados a objetos mediante clases, en forma de listas enlazadas.

La particularidad de una estructura de datos de cola es el hecho de que sólo podemos acceder al primer y al último elemento de la estructura. Así mismo, los elementos sólo se pueden eliminar por el principio y sólo se pueden añadir por el final de la cola.

en pocas palabras…

Colas:

Estructura lineal
Acceso de inserción por un extremo y
de eliminación por el otro extremo

Ejemplo de Cola

Ejemplos de colas en la vida real serían: personas comprando en un supermercado, esperando para entrar a ver un partido de béisbol, esperando en el cine para ver una película, una pequeña peluquería, etc. La idea esencial es que son todos líneas de espera.

Tipos de colas

Colas circulares (anillos): en las que el último elemento y el primero están unidos.

Colas de prioridad: En ellas, los elementos se atienden en el orden indicado por una prioridad asociada a cada uno. Si varios elementos tienen la misma prioridad, se atenderán de modo convencional según la posición que ocupen. Hay 2 formas de implementación:

Añadir un campo a cada nodo con su prioridad. Resulta conveniente mantener la cola ordenada por orden de prioridad.

Crear tantas colas como prioridades haya, y almacenar cada elemento en su cola.

Bicolas: son colas en donde los nodos se pueden añadir y quitar por ambos extremos; se les llama DEQUE (Double Ended QUEue). Para representar las bicolas lo podemos hacer con un array circular con Inicio y Fin que apunten a cada uno de los extremos. Hay variantes:

Bicolas de entrada restringida: Son aquellas donde la inserción sólo se hace por el final, aunque podemos eliminar al inicio ó al final.

Bicolas de salida restringida: Son aquellas donde sólo se elimina por el final, aunque se puede insertar al inicio y al final.

 

**Cola circular  🙂

Una cola circular o anillo es una estructura de datos en la que los elementos están de forma circular y cada elemento tiene un sucesor y un predecesor. Los elementos pueden cosultarse, añadirse y eliminarse unicamente desde la cabeza del anillo que es una posición distinguida. Existen dos operaciones de rotaciones, una en cada sentido, de manera que la cabeza del anillo pasa a ser el elemento sucesor, o el predecesor, respectivamente, de la cabeza actual.

anillo** en pseudocodigo :D

FUNC CrearCola() : TCola
VARIABLES
cola: TCola
INICIO
        cola.frente <- MAXCOLA
        cola.final <- MAXCOLA
        RESULTADO <- cola
FIN

PROC DestruirCola(&cola: TCola)
INICIO
//No hay q hacer nada
FIN

FUNC ColaLlena(cola: TCola): LÓGICO
INICIO
        RESULTADO <- (cola.final MOD MAXCOLA) + 1 = cola.frente
FIN

FUNC ColaVacia(cola: TCola): LÓGICO
INICIO
        RESULTADO <- cola.final = cola.frente
FIN

PROC MeterCola (&cola: TCola; &e: Telemento; &error: Terror)
VARIABLES
fin: NATURAL
INICIO
        SI ColaLlena(cola) ENTONCES
                error <- ErrorColaLlena
        EN OTRO CASO
                error <- NoError
                fin <- (cola.final MOD MAXCOLA) + 1
                cola.final <- fin
                cola.elementos[fin] <- e
        FINSI
FIN

PROC SacarCola (&cola: TCola; &e: Telemento; &error: Terror)
VARIABLES
        ini: NATURAL
INICIO
        SI ColaVacia(cola) ENTONCES
                error <- ErrorColaLlena
        EN OTRO CASO
                error <- NoError
                ini <- (cola.frente MOD MAXCOLA) + 1
                cola.frente <- ini
                e <- cola.elementos[ini]
        FINSI
FIN


Una cola de prioridades es una estructura de datos en la que los elementos se atienden en el orden indicado por una prioridad asociada a cada uno. Si varios elementos tienen la misma prioridad, se atenderán de modo convencional según la posición que ocupen.

en otras palabras ...

Una cola con prioridad es una estructura de datos lineal que devuelve los elementos de acuerdo a un valor asociado a ellos (prioridad)

(y no al orden en que fueron insertados). La prioridad puede coincidir con el valor del elemento, pero también puede diferir de él.

 

Este tipo especial de colas tienen las mismas operaciones que las colas , pero con la condición de que los elementos se atienden en orden de prioridad.

Ejemplos de la vida diaria serían la sala de urgencias de un hospital, ya que los enfermos se van atendiendo en función de la gravedad de su enfermedad.

Entendiendo la prioridad como un valor numérico y asignando a altas prioridades valores pequeños, las colas de prioridad nos permiten añadir elementos en cualquier orden y recuperarlos de menor a mayor.

Hay 2 formas de implementación:

Añadir un campo a cada nodo con su prioridad. Resulta conveniente mantener la cola ordenada por orden de prioridad.

Crear tantas colas como prioridades haya, y almacenar cada elemento en su cola.

Tipos

Colas de prioridades con ordenamiento ascendente: en ellas los elementos se insertan de forma arbitraria, pero a la hora de extraerlos, se extrae el elemento de menor prioridad.

Colas de prioridades con ordenamiento descendente: son iguales que la colas de prioridad con ordenamiento ascendente, pero al extraer el elemento se extrae el de mayor prioridad.

Operaciones

Las operaciones de las colas de prioridad son las mismas que las de las colas genéricas:

Crear: se crea la cola vacía.

Encolar: se añade un elemento a la cola, con su correspondiente prioridad.

Desencolar: se elimina el elemento frontal de la cola.

Frente: se devuelve el elemento frontal de la cola.

Destruye: elimina la cola de memoria.

 

La bicola o doble cola es un tipo de cola especial que permiten la inserción y eliminación de elementos de ambos extremos de la cola.

Puede representarse a partir de un vector y dos índices, siendo su representación más frecuente una lista circular doblemente enlazada.

Todas las operaciones de este tipo de datos tienen coste constante.

Ver animaciones de colas:
http://courses.cs.vt.edu/
csonline/DataStructures/
Lessons/
QueuesImplementationView/
applet.html
http://www.cs.usask.ca/
resources/tutorials/
csconcepts/1998_2/queues/java/

Referencias

http://www.it.uc3m.es/java/prog/units/pilas-colas/guides/index_es.html

http://www.it.uc3m.es/java/prog/units/pilas-colas/guides/index_es.html#idp22688

http://www.it.uc3m.es/java/prog/units/pilas-colas/guides/1/guide_es.html

http://es.wikipedia.org/wiki/Pila_%28estructura_de_datos%29

http://es.wikipedia.org/wiki/Cola_%28estructura_de_datos%29

http://es.wikipedia.org/wiki/Cola_circular

http://es.wikipedia.org/wiki/Cola_de_prioridad_%28estructura_de_datos%29

http://es.wikipedia.org/wiki/Bicolas

 

Anteriores Entradas antiguas