Algo II - Resumen Teóricas

download Algo II - Resumen Teóricas

of 101

Transcript of Algo II - Resumen Teóricas

  • 8/18/2019 Algo II - Resumen Teóricas

    1/101

    Especificación

    Algoritmos: secuencia ordenada y finita de pasos que realiza un cómputo dado un estadoinicial, una vez ejecutados los pasos se llega a un estado final.En el contexto de la materia, un problema es una descripción abstracta de un objetivo aalcanzar a partir de ciertas condiciones iniciales.Y su solución será un algoritmo que satisfaga esa descripción, es decir, que dados los datos

    en cuestión, alcance el objetivo descrito.De esta forma, la especificación de un problema puede ser entendida como un estadoinicial y un estado final.

    El estado inicial (potenciales entradas) y final (salidas esperadas) de un algoritmo, no esuna declaración de intenciones, sino un contrato que se debe cumplir antes de invocar a la

     función.

    Estado inicial: Delimitación del universo sobre el cual puede aplicarse la función. No esque puedo aplicarla pero doy un mensaje de error. Programas que transforman el estado deuna máquina de cómputo no hablan de la relación con el usuario. Mensajes de error yexcepciones son otro tema.

    El estado inicial es {true} cuando siempre puedo aplicar esa función sobre cualquier“entrada” (la restricción implícita estaría en los datos de entrada, por ejemplo, deben sernúmeros).

    Hay que encontrar una forma de formalizar matemáticamente una pieza de software.Programming in the large ( gran equipo de trabajo, división en módulos).

    Las herramientas que utilizamos deben preservar una relación con los problemas quequeremos resolver.

    Tipo Abstracto de Datos (TAD):Es una herramienta que sirve para recortar la realidad. Caracterización axiomática de unaclase de álgebra.

    Caracteriza en forma rigurosa y formal los aspectos “relevantes” de un fragmento de larealidad.Tipo en tanto a que identifica la colección de “cosas” que comparten el comportamientodescrito.

    Es una herramienta matemática que nos da la rigurosidad y formalidad necesarias paradescribir el problema a resolver.

    Desde el punto de vista histórico es uno de los primeros intentos por abordar la problemática de describir formalmente el comportamiento de una pieza de software.

    Construimos “álgebras” que se comportan “como” (reflejan la info algebraica que subyaceal problema).

  • 8/18/2019 Algo II - Resumen Teóricas

    2/101

    Hay de desarrollar una especie de sentido común, saber cuales son los factores relevantes ytratar de embeberse del dominio del problema. Observar el comportamiento colectivo dedeterminados objetos del mundo real. Tratar de caracterizar las cosas y cómo se relacionanentre sí para resolver el problema.Queremos expresar nuestro problema a partir de los tipos de las cosas que intervienen en él.Establecer una caracterización formal del comportamiento algebraico.Partimos de un conjunto de reglas que nos permiten construir un tipo de datos.

    ¿Cuáles son los conceptos que intervienen en el proceso del problema?

     No prefijar una estructura antes de saber el problema, porque prefijamos una única manerade resolverlo a partir de la estructura elegida.

    Identificamos distintos tipos. La separación permite pensar en forma modular el problema a partir de problemas de menor complejidad cuyas soluciones se pueden componer pararesolver el problema original. Luego resta saber como caracterizar formalmente elcomportamiento de cada tipo identificado.

    Ejemplo: tenemos la percepción de que es una facultad de los números ser sumables.Ponemos en el centro del universo las cosas que nos interesan, y estas tienen propiedades

    que les son intrínsecas.

    Lógica trivaluadaSe tienen valores de verdad: true, false e indefinido.Indefinido significa que algo NO puede ser evaluado computacionalmente.Introducimos nuevos operadores lógicos que consideran esta situación al ser evaluados.

    Operadores básicos: forzamos una decisión en función de la info que tenemos.Embebimos la noción de orden en el comportamiento (¿?)Por ejemplo tenemos la función:F(a,b,c) = (b!=0) ˄L (a/b == c) que nunca evalúa a indefinido.

    Funciones totales: cuando uno las define, están definidas para todo el dominio).

    Sean {Ti} con 0

  • 8/18/2019 Algo II - Resumen Teóricas

    3/101

     Los nombres no determinan unicidad, hay otras cosas que se comportan igual.

    Las variables van a ser consideradas como términos del lenguaje.

    Biyección: si todos los elementos del conjunto de salida tienen una imagen distinta en el conjunto de llegada, y acada elemento del conjunto de llegada le corresponde un elemento del conjunto de salida.

    => Si hay biyección tengo suficientes nombres para todos los ”elementos” 

    Sean X un conjunto de variables (cada una con un tipo) y {fi} con 0 ¬ P, P ˅ Q, P ˄ Q, P ˄L Q, P ˅ L Q, Ǝ x P ˄  x Pϵ a Form(X, {fi})

    Se denomina sentencia a una fórmula sin variables libres

    En una gran cantidad de casos se pretende evaluar sobre un subconjunto determinado de lostérminos de un tipo.Los luegos se utilizan para restringir el dominio sobre el que se aplican las cosas. P esla restricción del dominio Q:

    (Ǝ x:T) (P(x) ˄L Q(x)) vale si existe un término T tal que, además se encuentra en elsubconjunto de los que satisfacen P y además satisface la propiedad Q.

    (Ǝ x:T) (¬P(x) ˅ L Q(x)) vale si la totalidad de los términos en T que se encuentran en elsubconjunto de los que satisfacen P además satisfacen la propiedad Q. 

    La existencia de algo solo puede estar argumentada en tanto ese algo pueda serexpuesto. Si el universo está vacío no puedo mostrar un elemento de ese existencial.

    Vamos a encapsular el comportamiento dentro de los tipos de datos.

    TDA:

    NOTA: = es una función definida que devuelve TRUE o FALSE. ≡ es un igualador que se deriva de losaxiomas (es demostrable como igual a partir de estos)

  • 8/18/2019 Algo II - Resumen Teóricas

    4/101

    Género: Nombre que va a adoptar nuestro tipo de datos (conjunto de valores del tipo). Engeneral va a haber uno solo, pero podrían ser más.Exporta: Detalla qué operaciones y géneros se dejan a disposición de los usuarios del tipo,sería el equivalente a la parte pública de las clases. Exportamos el tipo de datos queestamos construyendo para que otros lo puedan usar.

    Parámetros formales: Singulariza las propiedades que debe satisfacer el género sobre el

    que es paramétrico el tipo. Ejemplo: conj ( α ) ¿α sería el parámetro formal? TAD secu[T]Género secu [T]

    Generadores: Son las funciones/operaciones que permiten construir los términos del tipo(crear nombres para los individuos con los que vamos a trabajar). Tengo que ser capaz deconstruir al menos tantos términos como los que quiera denotar (todo término del tipo debe

     poder ser construido a través de una combinación del conjunto de generadores => escompleto)Si agrego más generadores de los necesarios puedo tener “sinónimos” y luego deboasegurar que los sinónimos siempre sean equivalentes. Puede pasar que el conjunto degeneradores naturalmente tenga sinónimos (por ej, el conjunto - Un conjunto no tiene orden

    entre los elementos, y no tiene elementos iguales). Minimalidad!Minimal no es lo mismo que mínimo, minimal quiere decir que no tiene que sobrar ninguno,no necesariamente es mínimo.

    Los generadores no definen el comportamiento, solo construyen “elementos observables”

    Observadores básicos: Son aquellas operaciones que, utilizadas en conjunto, nos permitendistinguir si dos instancias del tipo son iguales o no.Son funciones capaces de distinguir toda característica relevante acerca de los términos.Permiten mirarlos y extraer información. En conjunto identifican al término y los separanentre los que tienen igual o distinto comportamiento. (dos cosas observacionalmenteiguales deben comportarse igual) 

    Es importante elegir un conjunto razonable (minimal) de observadores.

    Son las propiedades que nos interesan de las cosas. La observación está estrictamenterelacionada con la estructura sintáctica del elemento.Esto se rompe con el conjunto porque por ejemplo:{a} U ({a} U C) =obs {a} U C

     No hay unicidad de nombres, tengo dos nombres distintos para lo mismo. Este es el núcleopara comprender la semántica observacional.

      Semántica lógi ca, desarrolla una serie de problemas lógicos de significación, estudia la relación entre

    el signo lingüístico y la realidad. Las condiciones necesarias para que un signo pueda aplicarse a un objeto, y

    las reglas que aseguran una significación exacta.  Sintaxis: cómo definir fórmulas bien formadas (fórmulas como cadenas de símbolos – alfabeto, lenguaje)

  • 8/18/2019 Algo II - Resumen Teóricas

    5/101

      Semántica: cómo interpretar esas fórmulas, como asignarles un valor de verdad (fórmulas como

    enunciados que pueden ser verdaderos o falsos – valuaciones)

    Al permitir poner de relieve las características que distinguen dos términos, es posibleformular una noción de igualdad basada en estas características:

    Igualdad observacional: t es igualmente observacional a t’ entonces t es indistinguible det’ a partir de las funciones observacionales, y para todo f, f(t) es igualmente observacional af(t’), si no, f no es función.Dos términos son iguales sii se verifica un predicado descrito en función de losobservadores básicos.

    Propieda reflexiva La propiedad reflexiva establece que para cada número real  x, x = x.

    Propiedad simétrica La propiedad simétrica establece que para todos los números reales x y y,si x = y, entonces y = x.

    Propiedad transitiva 

    La propiedad transitiva establece que para todos los números reales x, y, y z ,si x = y y y = z , entonces x = z .

    Propiedad de sustitución Si x = y, entonces x puede ser reemplazada por y en cualquier ecuación o expresión.

    La igualdad observacional cumple con una relación de equivalencia reflexiva, simétrica ytransitiva.Tiene que ser una congruencia (más fuerte que equivalencia), si t =obs t’ entonces para todafunción f(t) =obs f(t’) si no se cumple esto, no es una función

    Todos los términos que trabajamos son construcciones finitas (si voy achicando el términollego a un caso base)

    Si algo en mi “término/instancia” del TAD cambia y yo lo puedo ver, entonces no es

    el mismo término. Para esto sirven los observadores.

    Igualdad sintáctica: los objetos son distinguibles a partir de tener nombres diferentes.

    Otras operaciones: son aquellas operaciones adicionales que caracterizan algúncomportamiento esperado del tipo. Deben ser consistentes con lo que me responden losobservadores. Respetar la congruencia.

     Deben brindar usabilidad. Los generadores y observadores no le importan al usuario

    Axiomas: Son las reglas que definen matemáticamente el comportamiento de las

    operaciones identificadas.

  • 8/18/2019 Algo II - Resumen Teóricas

    6/101

    Usa: manera de importar otro tipo de datos.

    Metodología

    La igualdad observacional o en su defecto los observadores básicos son un punto de partida ideal. Debido a que identifican cuando dos cosas son iguales (o diferentes) exhibentodo el comportamiento relevante del tipo.Luego debemos elegir un conjunto completo de generadores. Operaciones que nos

     permiten construir todos los términos de interés del tipo.Los axiomas deben ser razonables: 

    1. El conjunto de axiomas es completo para el conjunto de términos.2. No hay dos axiomas que apliquen y den resultados diferentes.3. Las recursiones terminan.

    Debe haber completitud: un axioma por cada generador identificado, de modo que paratodo término la operación este definida. (Siempre que haya un término y una funciónque tenga que estar definida sobre el mismo, haya un axioma que aplique)

    No ambiguos: Para cada término construible un y solo un axioma de la definiciónaplica, luego no es posible obtener valores diferentes.Terminación: Tener bien definidos los casos bases y para todo axioma recursivo eltérmino de la derecha es de menor complejidad que el de la izquierda de modo que no

     pueda existir una recursión infinita. Entonces tenemos computabilidad de los resultadosque queremos. Establece un pie a partir del cual la implementación es posible a travésde algoritmos.Los observadores básicos siempre se axiomatizan sobre los generadores para definir elcomportamiento elemental del tipo.Idealmente las otras operaciones deben axiomatizarse sobre los observadores básicos.De esta forma se garantiza que nunca una de estas operaciones puede distinguir doscosas que son iguales

     ESTO CIERRA CON EJEMPLO DE CONJUNTO VER HOJAS 6 y 7 DE LACARPETA

    INDUCCIÓN

    Inducción y recursión son dos caras de la misma moneda, ¿Cuándo? ¿Qué? => derivarestructuralmente propiedades.Extrapolar una propiedad de un subtérmino al término completo.Extraer propiedades generales a partir de observaciones particulares.

    La inducción es un método (científico) que se basa en derivar reglas generales a partir de:- desde un punto de vista histórico: observaciones particulares.- Desde un punto de vista matemático… a partir de probar que: si una propiedad vale paraun elemento, entonces esto implica que vale para todo otro que lo contenga estrictamente.

    Comentario: Notas de lahoja 7. VER

  • 8/18/2019 Algo II - Resumen Teóricas

    7/101

     Uno tiene que construir el argumento formal por el cual las cosas valen, ejemplo:( n, m, p: nat) (suma(n, suma(m,p)) = suma(suma(n,m), p))Elijo una variable, la p. Esta ocupa el lugar donde los axiomas son inductivos(cuantificador). Mi recursión está hecha sobre uno de los parámetros.

    ( p: nat) (P (p))

    P(x) = ( n, m: nat) (suma(n, suma(m,x)) = suma(suma(n,m), x))Es una manera de formalizar la elección de la variable.

    Caso base / Caso inductivo

     La inducción estructural no me permite probar cosas sobre dos términos, tengo que

     probar la propiedad sobre uno (n, m o p en este caso)

    Con los árboles hay más de un paso inductivo

    Para que esto funcione debe tratarse de un conjunto bien ordenado. Esto es si existe unarelación binaria que:a) ordena totalmente los elementos y b) es una relación bien fundada.

    Se dice que una relación binaria en un conjunto es bien fundada si existe un elementomínimo 0 tal que para todo otro elemento t, el par no está en la relación.

     No se puede hacer un argumento similar para los números reales, para que funcione hayque tener un conjunto bien ordenado. La relación de orden entre los elementos delconjunto es porque necesitamos una noción de inclusión de términos.Que para todo par de elementos me pueda decir cual está antes o después (relacióntotal).El argumento se puede usar un numero finito de veces para que permita tener unargumento cerrado, entonces al final tengo un caso base (el mínimo en la relación deorden – el mínimo que obtengo al pelar una cantidad finita de veces)

    Funciona porque dado un orden entre los generadores, extendemos ese orden a secuencias

    finitas de generadores, es decir, si pensamos a los generadores como “letras”, construimosel “orden lexicográfico” de términos

    Primero los generadores base (pueden tomar argumentos de otro tipo pero no sonrecursivo), luego se ordenan los generadores recursivos y por último se construye unorden derivado de eso a “palabras” construidas con eso.Ej: 0 < Suc(0) porque 0 es < que Suc.Puedo construir un orden lexicográfico a partir del orden de los generadoresg1(g2(g3(…)))

    Lema: para todo par de términos t, t’ distintos, o bien t > t’ o bien t’ > tLema: Existe un elemento mínimo (se toma como dicho elemento al generador no

    recursivo que sea menor bajo la relación > - o “el que cayó primero según el orden quesortee)

    Comentario: Ese asuntocebolla

  • 8/18/2019 Algo II - Resumen Teóricas

    8/101

     Teorema: Los conjuntos de términos construidos con el lenguaje de TADs con el quetrabajamos son conjuntos bien ordenados.

    Corolario: Los conjuntos bien ordenados no poseen cadenas (respecto de >) descendentesinfinitas.

    Los términos tienen una estructura finita entonces no hay cadenas descendentesinfinitas ya que en algún momento termino.Si el argumento inductivo dice P(2) => P(3), probemos P(2) (modus ponens)P(0) P(0) => P(1) (m.p.)

    P(1) P(1) => P(2) (m.p.)Etc…

    Es una cadena descendente finita hasta le caso base.

    Las relaciones entre elementos de conjuntos se dan en muchos contextos y, en informática, aparecen confrecuencia en programación, bases de datos informáticas, etc.1.1. Relaciones binarias.Definición 1.1.1. Sean A y B dos conjuntos. Una relación (binaria) R de A en B es un subconjunto de A × B:R⊂ A × B = {(a, b)/a ∈ A, b∈ B}

    Escribiremos a R b para indicar que (a, b)∈ R y aR b 6 para expresar que (a, b)∈/ R. Si a R b diremos que aestá relacionado con b.Si R es una relación de A en sí mismo, i.e., R⊂ A × A, diremos que es una relación en A.

    El orden lexicográfico es una relación de orden que se utiliza para ordenar producto cartesiano de conjuntos

    ordenados. Es conocido principalmente por su aplicación a cadenas de caracteres, por ejemplo en diccionarios o en

    la guía telefónica.

    En lógica, modus ponendo ponens (en latín, modo que afirmando afirma), también llamado modus ponens y

    generalmente abreviado MPP o MP, es una regla de inferencia que tiene la siguiente forma:

    Si A, entonces B. // A // Por lo tanto, B

    Entonces: (a) las recursiones terminan siempre que tengan bien definidos los casos base yque los casos recursivos que toman un término t se resuelvan en función de un término t’ talque t > t’, y (b) tiene sentido pensar que si demuestro una propiedad para los casos base yque si vale para un término, entonces vale para los términos que lo contienen.

    Esquema de inducción:Sea P una fórmula con una única variable libre x de tipo T, luego, si queremos ver que Pvale para todo elemento de T, debemos probar:

    ( x:T) P(x)

    Sea T un tipo con generadores

  • 8/18/2019 Algo II - Resumen Teóricas

    9/101

     C1: args -> T r1 : t x args -> T… ...Cn: args -> T rm : t x args -> T

    Y P una propiedad con una variable t de tipo T

    El esquema se deriva a partir de los generadores. Si pruebo la conjunción de la propiedad sobre todos los casos base y la conjunción de todos los recursivos. La propiedad se expresa en funciones y las funciones respetan la igualdad observacional.Entonces si vale para uno, vale para todos los demás.

    Implementaciones de pilas, colas y afines. Memoria dinámica

    Pila:Observadores básicos: vacía?, tope, desapilarGeneradores: vacia, apilarOtras operaciones: tamaño

    Cola:Observadores básicos: vacía?, próximo, desencolarGeneradores: vacia, encolarOtras operaciones: tamaño

    Las pilas y las colas son bastante similares por lo que vamos a trabajar con el TAD Cola.A las pilas se las llama colas LIFO (last in, first out) y a las colas, colas FIFO (first in, firstout).

    Una implementación posible es usando arreglos. Una cola sería un arreglo, más un naturalque indica su tamaño. Las posiciones del arreglo de n elementos van de 0 a n-1 (porconvención en este caso).Instanciamos α en floatsStruct {

     Nat cant;Flota elementos[MAX_CANTIDAD]

    }

    Idea: los elementos válidos son los que figuran entre la posición 0 y cant-1.

  • 8/18/2019 Algo II - Resumen Teóricas

    10/101

    Una posible implementación de desencolar es, al sacar el primer elemento, correr todos una posición. Esta no es una buena implementación ya que es extremadamente “cara”. Cuantosmás elementos tenemos, más tarda.Se podría solucionar agregando a la estructura la variable primero (nat) de modo que enlugar de que el primero sea siempre 0, vayamos “corriendo” esta posición.Ahora, a diferencia de la implementación anterior, en esta solo voy a poder meterMAX_CANT elementos. En la anterior este era el límite de elementos que podían convivir

    en simultaneo acá es el límite total.Una posible solución sería desplazar los elementos en algún momento. Solución másinteresante:

    Usando aritmética circular (módulo). Usamos la misma estructura pero podemos seguirusando las posiciones que están antes de c.primero de modo que la posición a encolar en laimplementación sería (c.primero + c.cant) mod MAX_CANTIDAD y la posición adesencolar sería (c.primero + 1 ) mod MAX_CANTIDAD.

    En matemática, la aritmética modular  es un sistema aritmético para clases de equivalencia de números

    enteros llamadas clases de congruencia. 

     Algunas veces se le llama, sugerentemente, aritmética del reloj, ya que los números «dan la vuelta» tras alcanzar

    cierto valor llamado módulo.

    2

     La aritmética modular puede ser construida matemáticamente mediante la relación de congruencia (elación entre

    dos números enteros a y b que tienen el mismo resto mediante una división euclidiana1 por un número natural mdistinto de 0) entre enteros, que es compatible con las operaciones en el anillo de enteros: suma, resta, ymultiplicación. Para un determinado módulo n, ésta se define de la siguiente manera:3 

    a y b se encuentran en la misma "clase de congruencia" módulo n, si ambos dejan el mismo resto si

    los dividimos entre n, o, equivalentemente, si a − b es un múltiplo de n.

    Esta relación se puede expresar cómodamente utilizando la notación de Gauss: a = b (mod n)

     Así se tiene por ejemplo 63 = 83 (mod 10)

    ya que ambos, 63 y 83 dejan el mismo resto (3) al dividir entre 10, o, equivalentemente, 63 − 83 es un múltiplo de

    10. Se lee:3 «63 es congruente con 83, módulo 10», o «63 y 83 son congruentes uno con otro, módulo 10».

    Con esta misma idea podríamos implementar una secuencia, pero la operación que eliminaa un elemento de la misma sería cara porque involucraría desplazamientos para no dejarhuecos.En cuanto al tamaño (si no alcanza, o sobra). Algunos lenguajes proveen arreglosredimensionables, el mecanismo consiste en crear uno más grande y luego copiar loselementos.

    Hasta acá estamos trabajando con memoria estática, asignada por el compilador alcompilar el programa.Al redimensionar lo que se hace es conseguir un espacio más grande en la memoria yvolver a copiar todo en el nuevo espacio.

    Memoria Dinámica

  • 8/18/2019 Algo II - Resumen Teóricas

    11/101

     Es la memoria que le pedimos al sistema operativo cuando el programa ya se estáejecutando. Tanto el espacio como lo que contiene es la variable:

    Variable matemática: Valor fijo pero desconocido. Ejemplo: ( x) ( x + 1 > x)Variable computacional: Objeto que contiene un valor.

    Z = x + 8 z sería el “cajón” y x el valor.A la izquierda de la asignación las variables son lugares de memoria. A la derecha, sonel valor de su contenido.

    Abstractamente la memoria es un vector de “bytes”. Ejemplo [0…64mb]Las variables tienen un tipo que determina, entre otras cosas su tamaño (es decir el espacioque esa variable ocupa.A cada programa en ejecución le corresponde un fragmento, dado por las variables estáticasque utiliza.Si tenemos por ejemplo un programa con dos variables int y un float, y suponemos que unint usa 2 bytes y un float 4, ese programa usa 8 bytes de memoria: M[comienzo…comienzo + 7].

    Conclusión: podemos utilizar una variable para referirnos a su valor o al espacio dealmacenamiento que representa (x = 3, z = 5, z = x).

    En general las posiciones de memoria son consecutivas, a nosotros no nos importa.¿Cómo me doy cuenta de cuál es el espacio de memoria que el S.O. me dio? ¿cómo louso?Cuando le pido memoria al sistema operativo le paso una variable que ya tenía definidaen mi programa (puntero) y le digo que me devuelva ahí la ubicación del espacio queme dio en la memoria (es decir, dónde empieza el espacio).La memoria se direcciona usando números enteros.Si pierdo el puntero no puedo usar la memoria ni liberarla.

    Hasta ahora vimos variables estáticas, existen también las variables dinámicas sirven,entre otras cosas, para los casos en que no sabemos de antemano el tamaño de la entrada(podemos por ejemplo pedirle al usuario una cantidad C de enteros que va a ingresar,reservar esa memoria, y luego leerlos y almacenarlos.

    Punteros 

    Sirven para referirnos a posiciones arbitrarias de la memoria y permiten utilizar estructurasdinámicas.

    En el “comienzo de los tiempos” se pedía memoria pidiendo una cantidad de bytes enuna variable/puntero. Uno indicaba la cantidad necesaria y se pierde la noción de tipado,

    no sé qué es lo que va a ocupar esa memoria.Int x

  • 8/18/2019 Algo II - Resumen Teóricas

    12/101

    Punt pX = 3*p = 7

    En c la asignación dinámica de memoria se hace con MALLOC (memory allocate,dame memoria (reservá, asigná) para alojar de tal tipo tal cantidad.

    P = MALLOC(int, 100).

    Sobrescribir direcciones de memoria es una técnica muy común de intrusión.

    Puntero tipado:Puntero a int pP = MALLOC(100) -> no hace falta decirle el tipo porque ya lo sabe

    En C++ malloc es new.

    Para ver el valor al que apunta p: x = *p.(*p).campo equivale a p->campo.Si hago p= 400 ahora p apunta a M[400]

    Si hago x= 398; p= x ahora p apunta a M[398]Para asignar un valor debo hacer p = &x; (p apunta a la “celda” llamada x)

    & me devuelve la dirección de memoria en la que “vive” una variable.P = &xAliasing: dos nombres hacen referencia al mismo objeto. P apunta a x entonces *p y xson dos nombres para lo mismo.Si hago *p = &x pongo x en su misma dirección de memoria.

    La memoria que pido la tengo que liberar: malloc/free new/deleteSi no la libero depende del sistema operativo. Hoy en día el SO libera la memoriacuando termina el programa. Windows 3.11 no liberaba memoria.Con memoria dinámica, el programa en ejecución puede usar más memoria que la

    disponible en el sistema (¿?¿?)

    Implementación de colas con memoria dinámica y punteros:

    Struct {Struct nodo_cola *prim;Struct nodo_cola *ult;

     Nat cant;}

    struct nodo_cola{float elem;Struct nodo_cola *prox;

    };

    Comentario:Chequear e porque no se si anoté bien

  • 8/18/2019 Algo II - Resumen Teóricas

    13/101

    Prim apunta al primer elemento encolado de los que quedan (el más viejo, el próximo asalir). Ult apunta al último elemento encolado (el más reciente) y cada nodo tiene un

     puntero al elemento anterior.

    El último elemento apunta a NULL (una dirección de memoria que apunta a nada).Ahora por cada elemento tengo casi el doble de memoria que la implementaciónanterior.

    En un arreglo puedo acceder directamente al iésimo elemento, mientras que acá tengoque recorrer uno por uno hasta llegar al iésimo.

    Función auxiliar: Nuevo_nodo(elemento):Struct nodo_cola *nodo;

     Nodo:= new (struct nodo_cola);If (nodo == null) Hacer_algo_com_el_problema(); Nodo->prox := NULL;

     Nodo->elem := elemento;Return nodo;

    Es una función que pide memoria y luego guarda el elemento devolviendo un punterohacia el mismo.Si nodo==NULL quiere decir que no nos dio el espacio en memoria. En sowftwarescríticos (como por ejemplo el de un marcapasos) no es conveniente usar memoriadinámica. Se utiliza memoria estática.

    Para desencolar, guardo el c.prim en un auxiliar (guardo a dónde apuntaba) y después lo borro (libero memoria)

    Aux:= c.prim;c.prim:= c.prim->prox;delete aux;

    En el caso de la cola es razonable tener un único puntero por nodo porque nos movemos

    unidireccionalmente. Para mayor flexibilidad existen también las listas doblementeenlazadas (dos punteros, anterior y próximo).

    Tarea: Programar una cola que se comporte como FIFO o LIFO de acuerdo a un parámetroal constructor (en tiempo de ejecución).

    La implementaría sobre una lista doblemente enlazada. Agregar elemento agregasiempre atrás, y si es FIFO en el caso de que el próximo sea NULL pone próximo alque agrega y si no lo deja como está y el último es siempre el que agrega, si es LIFOcada vez que agrega un elemento lo pone como próximo y el último es el que agregasi es NULL o el que ya estaba si no era null.Al sacar un elemento saca siempre el próximo y si es FIFO pone como próximo elsiguiente y si es LIFO pone como próximo el anterior. 

    COMPLEJIDAD

  • 8/18/2019 Algo II - Resumen Teóricas

    14/101

     Desde un punto de vista práctico no todo algoritmo que satisface una especificación da lomismo.Existen dos tipos de complejidad, la temporal y la espacial, y sirven para saber cuantonos cuesta resolver un problema en tiempo y espacio respectivamente.En la materia solo nos preocuparemos por la complejidad temporal.

    Medidor de recursos: ¿Cuánto tiempo el cpu te asignó? Solo considera lo específico demi programa (la ejecución). No depende de todo lo que se está ejecutando pero sí de esacomputadora específica.Lo que debe determinar el comportamiento son las estructuras de control que estoyusando en mi programa y no el lenguaje. Se fija un paradigma independizándonos dellenguaje.

    ¿Cómo podemos medir el costo temporal de un algoritmo?

    1. Usando un cronómetro Se suele llamar wall time. Lo bueno es que nos diceobjetivamente cuánto tarda, lo malo es que depende de factores completamente ajenos al

     programa y los datos. Ni siquiera es confiable entre dos ejecuciones consecutivas.

    2. Usando un medidor de recursos Se suele llamar CPU time. Lo bueno es que nos dicecuántos recursos utilizamos, lo malo es que depende de la computadora específica; luego simañana cambio de computadora, cambia el comportamiento.¡Contando operaciones elementales!Se suele llamar complejidad algorítmica. Se trata de acotar la cantidad de operacioneselementales que toma resolver un problema en función del tamaño de la entrada.

    Deberíamos encontrar una métrica que sea independiente de la “máquina” en la que seejecuta e incluso del lenguaje que se está implementando.

    Ejemplo A:

    void max_min (int *datos, int cant, int &max, int &min){

    max = datos[0]; c1for (int i = 1; i < cant; i++) cant*c2if (max < datos[i]) then max = datos[i]; cant*c3min = datos[0]; c4 = c1for (int i = 1; i < cant; i++) cant*c5 = cant*c2if (min > datos[i]) then min = datos[i]; cant*c6 = cant*c3}

    Las constantes están vinculadas a un determinado procesador, memoria, etc. Queremosuna métrica en función del tamaño de entrada, es lo que determina el costocomputacional. Entonces, vamos a construir una abstracción matemática.

  • 8/18/2019 Algo II - Resumen Teóricas

    15/101

    Las constantes reflejan el costo de las operaciones elementales en el lenguaje y plataforma puntual en la que se ejecuta el algoritmo. Es decir, aquello de lo que queremosabstraernos…

    Clases de funciones:Para poder abstraernos de las constantes, debemos poder clasificar las funciones de acuerdoa su razón de crecimiento…

    Para ello, lo que se hace es determinar cotas ajustadas por encima y por debajo que permiten reflejar esta razón.

    Ejemplo Bvoid max_min (int *datos, int cant, int &max, int &min){max = datos[0], min = datos[0]; 2*C1for (int i = 1; i < cant; i++){ cant*C2if (max < datos[i]) then max = datos[i]; cant*C3if (min > datos[i]) then min = datos[i]; cant*C3}}

    ¿Cuánto crece el costo en función de la entrada? En C3 por ejemplo hay dosoperaciones, el if y el then, y lo que pasa en el then no se ejecuta siempre. La ejecucióno no, no depende del algoritmo sino de los datos de entrada.

    En el mejor de los casos la cantidad de veces que se ejecuta el then podría ser 0 y nuestroalgoritmo sería más eficiente.En el peor de los casos podría ser cant y nuestro algoritmo tendría su peor rendimientoO podríamos asumir que es cant/2 en un caso promedio y tendríamos una idea general desu rendimiento.

    Cada una de estas suposiciones nos brinda una mirada sobre el comportamiento de nuestroalgoritmo:

    Peor: tendremos la certeza de cuál es el máximo tiempo necesario para ejecutar nuestroalgoritmo.

    Es una certificación muy fuerte: nunca va a costar más que esto.Mejor: nos brinda una cota de los más rápido que nuestro algoritmo consigue ejecutar.

    Es una cota inferior, no es una garantía, pero tendremos que esperar por lo menos eso.Promedio: Nos proporciona una mirada equilibrada entre peor y mejor caso del tiempo quetoma ejecutar nuestro algoritmo, esto casi siempre depende de hipótesis adicionales sobrelos datos.

    En general vamos a adoptar la postura de calcular el peor caso. Y en principio eseanálisis se va a calcular a partir de probar la pertenencia de una función a una ciertaclase de funciones.

    Para cada uno de estos análisis se puede definir una clase de funciones que se comportan“igual” a una dada y así probar si nuestro algoritmo pertenece o no a dicha clase,caracterizando así su complejidad temporal

  • 8/18/2019 Algo II - Resumen Teóricas

    16/101

     

    La clase O:O(g(n)) = { f(n) | (Ǝ c, x0) ( x0

  • 8/18/2019 Algo II - Resumen Teóricas

    17/101

     Aritmética de órdenes de complejidad

    Ahora tomamos el ejemplo A y reemplazamos todas las constantes por O(1).O(1) es el costo de las operaciones elementales. Si hay una invocación a otra función dela cual conozco su orden de complejidad podría ser O(n). Las constantes no importan,sólo el orden de complejidad.

    Tenemos O(1) + cant*O(1) + cant*O(1) + O(1) + cant*O(1) + cant*O(1) (*1)

    Como hacer para que esto se convierta en una única expresión O(f(n)) que caracterice sucosto.

    f(n) + O(g(n)) = O(f(n) + g(n))f(n) * O(g(n)) = O(f(n) * g(n))O(f(n)) + O(g(n)) = O(f(n) + g(n))O(f(n)) * O(g(n)) = O(f(n) * g(n))

    Los logaritmos son todos iguales a efectos del cálculo de complejidad temporal:

    O(log(a)(n)) == O(log(b)(n) / log(b)(a)) == O(1/log(b)(a) * log(b)(n)) == O(log(b)(n))

    (*1) queda entonces como O(cant).La función O absorve las operaciones aritméticas, incluso colapsa las clases defunciones. La multiplicación responde a la necesidad de una estructura de control que seva a repetir una cierta cantidad de veces (un ciclo).La suma, por la composición secuencial:

    P;Q;O(P) + O(Q)

    O son funciones en una variable, no hay manera de ¿absorber? Una variable sobre otra.G podría ser en otra variable pero…???El if no está representado, pero no importa. Agarramos la rama más costosa ycalculamos sobre eso.

    Ecuaciones de recurrencia

    Se denomina ecuación de recurrencia a una igualdad de la forma:

    T(n) = f(n) * T(g(n))+ h(n)Denotando que la resolución de un problema de tamaño n requiere resolver f(n)subproblemas de tamaño g(n) tal que la integración de sus soluciones cuesta h(n).

    Ejemplo: T(n) = 2 T(n/2) + n es la ecuación de ordenamiento para Merge Sort. Agarraun arreglo que quiero ordenar y si el tamaño es mayor a 2 lo puedo partir en 2 y llamo

    Comentario:Muy en la todo esto, tendría que ver qquiere decir

  • 8/18/2019 Algo II - Resumen Teóricas

    18/101

    recursivamente, ordeno una mitad, ordeno otra y después las voy barajando. Dado unarreglo de tamaño n ordena dos arreglos de tamaño n/2 y luego invierte n en ordenarlos.Para resolver el más grande veo cuanto me cuesta resolver uno un poco más chico.

    Entonces, nos gustaría dada una ecuación de recurrencia, poder dar una función f(n) tal queT(n) = x0 T(n)

  • 8/18/2019 Algo II - Resumen Teóricas

    19/101

     T(n/24) T(n/24) T(n/24)

    Los pasos van de 0 (T(n)) en adelante. Como siempre lo divido por 4 la relación entre la

    altura y n es log4 n. n = 4h En cada paso tengo 3h veces T(algo)El último nivel es el que me muestra el costo de todos los casos base. Entonces.Resolver el todos los casos base es:

    3 log4 n . O(¿) ->costo del caso base, asumimos que es una constante

    3 log4 n + ∑i= 1 hasta log4 n 3

    i-1 (n/ 4 i-1)2 

    3 log4 n + n2 ∑ i= 1 hasta log4 n (3

    i-1 / 16 i-1)

    3 log4 n + n2 ∑ i= 1 hasta log4 n – 1 (3 / 16 )

    Es una Progresión geométrica que finalmente resulta en O(n) (ver resolución en hoja 12carpeta)

    Serie geométrica 1 + 1/2 + 1/4 + 1/8 + ... converge a 2.

    Una progresión geométrica es una secuencia en la que el elemento se obtiene multiplicandoel elemento anterior por una constante denominada razón o factor  de la progresión. Se suele

    reservar el término progresión cuando la secuencia tiene una cantidad finita de términosmientras que se usa sucesión cuando hay una cantidad infinita de términos, si bien, estadistinción no es estricta.

     Así, 5,15,45,135,405… es una progresión geométrica con razón igual a 3, porque cadaelemento es el triple del anterior. Se puede obtener el valor de un elemento arbitrario de lasecuencia mediante la expresión del término general, siendo el término en cuestión, elprimer término y , la razón:

    En el ejemplo anterior, el cuarto elemento de la serie es:

    Ejemplos de progresiones geométricas

  • 8/18/2019 Algo II - Resumen Teóricas

    20/101

      La progresión 1, 2 ,4 ,8 ,16, es una progresión geométrica cuya razón vale 2, al igual

    que 5, 10, 20, 40.

      La razón no necesariamente tiene que ser un número entero. Así, 12, 3, 0.75,

    0.1875 es una progresión geométrica con razón 1/4.

      La razón tampoco tiene por qué ser positiva. De este modo la progresión 3, -6, 12, -

    24 tiene razón -2. Este tipo de progresiones es un ejemplo de progresiónalternante porque los signos alternan entre positivo y negativo.

      Cuando la razón es igual a 1 se obtiene una progresión constante: 7, 7, 7, 7 

      Un caso especial es cuando la razón es igual a cero, por ejemplo: 4, 0, 0, 0. Existen

    ciertos autores que no consideran este caso como progresión y piden explícitamente

    que en la definición.

    Suma de términos de una progresión geométrica[editar ] Suma de los primeros n términos de una progresión geométrica[editar ] Se denomina como Sn a la suma de los n primeros términos consecutivos de una progresióngeométrica:

    Si se quiere obtener una fórmula para calcular de una manera rápida dicha suma, semultiplica ambos miembros de la igualdad por la razón de la progresión r .

    puesto que

    Si se procede a restar de esta igualdad la primera:

    ya que todos los términos intermedios se cancelan mutuamente.

    Despejando

    De esta manera se obtiene la suma de los n términos de una progresión geométrica cuandose conoce el primer y el último término de la misma. Si se quiere simplificar la fórmula, sepuede expresar el término general de la progresión an como

    que expresa la suma de n términos consecutivos de una progresión geométrica en funcióndel primer término y de la razón de la progresión.Se puede generalizar el procedimiento anterior para obtener la suma de los términosconsecutivos comprendidos entre dos elementos arbitrarios (ambos inclusive):

    Suma de infinitos términos de una progresión geométrica

  • 8/18/2019 Algo II - Resumen Teóricas

    21/101

    Si el valor absoluto de la razón es menor que la unidad , la suma de los infinitostérminos decrecientes de la progresión geométrica converge hacia un valor finito. En efecto,

    si , tiende hacia 0, de modo que:

    Finalmente, la suma de los infinitos términos de una progresión geométrica de razón inferior

    a la unidad es:

    Método maestro:Es el “recetario” para resolver ecuaciones de recurrencia de la forma:

    T(n) = a T(n/b) + f(n)

    Sean a ≥ 1 y b > 1 constantes, sea f (n) una función, y sea T (n) la recurrencia que siguedefinida sobre los enteros no negativosT(n) = aT(n/b) + f(n),

    Entonces T (n) puede ser acotado asintóticamente como sigue:1. Si f (n) ϵ O(n log b

     (a)- ε) para ε  > 0, entonces T (n) ϵ (nlog b (a) ),

    2. Si f (n) ϵ (nlog b (a) ), entonces T (n) ϵ (nlog b

     (a) log n),

    3. Si f (n) ϵ O(n log b (a)+ ε) para ε > 0 y af (n/b) < cf (n) para alguna con- stante c > 1 y un n

    suficientemente grande, entonces T (n) ϵ a θ(f (n)).

    La intuición del teorema maestro recae en comparar qué parte del algoritmo domina elcosto del problema, es decir, comparar a) la base del árbol de recursión en donde seresuelven los casos base, o b) la función que integra las soluciones parciales en una

    solución final. Esta es la razón por la cual se compara f(n) con la cantidad de casos bases:n log b

     (a)

    En el primer caso mi función está acotada por encima por algo un poco más chico quela cantidad de casos base.En el segundo caso mi función está acotada por encima y por debajo por algoaproximadamente igual al caso base.Tercer caso está acotada por debajo por algo un poco más grande que la cantidad decasos base.

    Hay una salvedad importante para hacer: Como se trabaja con cotas asintóticas, lasdiferencias que se están caracterizando cuando se realiza la comparación deben ser

     polinomiales en n y por lo tanto, si esto no se cumple el teorema maestro no da respuesta ala caracterización de la complejidad temporal.Lo mismo ocurre con la condición de regularidad del caso 3.

  • 8/18/2019 Algo II - Resumen Teóricas

    22/101

     Es decir f(n) debe ser polinomial

    ¿Qué es la complejidad? Recursos: tiempo y memoria. Escrituras a disco en

     algoritmos más complejos (bases de datos) O(n 3 )en memoria frente a una escritura

    en disco es despreciable.

     El caso promedio depende también de los datos. La complejidad de las tablas de Hash

     se mide en el caso promedio. En términos de complejidad, logaritmos de cualquier base son equivalentes.

     En la búsqueda secuencial con alfa cambia la complejidad, por ejemplo, si son

     strings compararlos es O(tam(string))

     Podría hablar del mejor caso y acotarlo por abajo también. Omega significa acotar

     por abajo, pero no necesariamente es el mejor caso

    DISEÑO JERARQUICO DE TIPOS

    ¿Qué significa diseñar?* Pasar de la descripción del qué al problema del cómo

    * Considerar aspectos no funcionales, por ejemplo, eficiencia en el tiempo y espacio deacuerdo al contexto de uso* Permitir un cambio de paradigma (del utilizado para especificar al utilizado para

     programar) que resulte “ordenado”

    En nuestro caso se trata de producir una implementación en el paradigma imperativo de untipo abstracto de datos descrito en un lenguaje de características lógicas (en particularfuncional).

    ConjuntoDeNat se explica con  Conj[nat]

    T imp  se explica con  Ttads

    Las implementaciones se explican con una especificación. La relación no es simétrica (esdecir la especificación no se explica con una implementación)

    ¿Qué significa jerárquico?Que pensaremos la resolución del cómo a partir de representaciones de un tipo sobre otrosseparando responsabilidades en la construcción de la solución.

    ConjuntoDeNat se explica con  Conj[nat]

    Se representa con

    SecuenciaDeNat

    Se representa con

    Comentario: Estaría bueentender este chino-básico

  • 8/18/2019 Algo II - Resumen Teóricas

    23/101

  • 8/18/2019 Algo II - Resumen Teóricas

    24/101

    Si quiero que Ag(…) tenga orden 1 entonces no voy a poder hacer nada peor quemodificar el conjunto que estamos pasando por parámetro.F(x) = x2 f(2) = 4

     No transformó el 2 en 4, reemplaza y resuelve… ¿?En el sentido imperativo es más complejo, las variables denotan direcciones dememoria. Cada instrucción imperativa realiza una pequeña modificación en el estado demi pc.

    * Asignación.Acción primitiva por excelencia de los lenguajes imperativos, asignarle algo a lamemoria.C’Conjdenat c;C= c’;El significado depende de la semántica que le de al operador igual.Deep copy: me meto en el conjunto lo recorro y creo otro completamente independiente

     pero igual a c. = puede tener semántica de copia o semántica de referencia.

    Metodología de diseño

    Desde un punto de vista abstracto diseñar implica las siguientes tareas:* Elección del tipo a diseñarElegimos una estructura de representación y la vinculamos con la estructura queteníamos.

    * Introducción de los elementos no funcionalesLos aspectos no funcionales (ordenes de complejidad, no son solo el tiempo deejecución en función de la entrada ¿?) condicionan la elección de la estructura.

    * vinculación entre la representación y su abstracción* Iteración sobre los tipos restantes

    Conjdenat ^ sec conj[nat]

    Interfaz SRC

    Representación abs

    SecudeNat ^ secu[nat]

    PunterosEs correcto, el triángulo conmuta.

    Una operación binaria es conmutativa cuando el resultado de la operación es el mismo, cualquiera que sea elorden de los elementos con los que se opera 

    ConjdeNat implementa la algorítmica de conjunto sobre un contenedor que es lasecuencia. Internamente lo resuelvo de esa manera. La secuencia me sirve para

     preservar la coherencia de los punteros. Puede haber muchos niveles.

    Comentario: Esto último

    muy bien a qué venía.

    Comentario:Acá, hoja 1varias cosas que me quedaraire…y no pasé. Esto por ede la carpeta parecieracorresponder más bien a” Lde diseño”

  • 8/18/2019 Algo II - Resumen Teóricas

    25/101

    Efectivamente cada nivel de abstracción debería resolver una parte del problema. Sitodas las operaciones deben hacerse en ambos niveles, uno de los dos sobra.Puedo ir de conjunto directo a punteros, pero podría haber separado más las unidades

     para que quede más claro

    Los aspectos de la interfaz de un tipo describen todo elemento relacionado con losaspectos de uso de dicho tipo, es decir, toda cuestión referida a lo que resulte visible desde

    “afuera”.

    Las pautas de implementación serán todo aspecto que refiera a cuestiones vinculadas alos medios a través de los cuales el tipo garantiza esos aspectos de uso. (la implementaciónhacia adentro).

    La definición de la interfaz de un módulo de diseño implica tomar en cuenta varias cosas.Esencialmente debe explicarle al eventual usuario todos los aspectos relativos a losservicios que exporta.

    Documentación

    Servicios exportados: Describe para cada operación su orden de complejidad, aspectos dealiasing, efectos colaterales sobre los argumentos, etc.Para toda operación del tipo que vamos a diseñar escribir aspectos relevantes.Ejemplo: Conj Ag( , int n)Doble referencia, lo que pasé como valor y el valor modificado. Entonces hay un únicoconjunto que tiene n.Un aspecto de aliasing es de pronto desde adentro del conjunto tengo un puntero a otraestructura. Tiene que estar informado ya que después no puedo destruirlo. Hay quesaber si se agrega por aliasing o por copia. Si es por copia, la responsabilidad de liberarla memoria es mía.Efectos colaterales: modificar argumentos pasados por parámetro.

    Interfaz: define el paradigma imperativo, las operaciones exportadas junto con su

     precondición y su poscondición. Esto establecerá la relación entre la implementación de lasoperaciones y su especificación.

    Las precondiciones y poscondiciones se escriben en lenguaje lógico.Si el lenguaje de implementación es diferente al lenguaje de especificación hay unadiferencia entre los valores que pueden tomar las variables imperativas respecto de lostérminos lógicos.Para vincularlos utilizamos la función ^ que para cada valor imperativo nos retorna untérmino lógico al cual representa, de forma que este pueda participar de los predicadoslógicos que definen el comportamiento formal de las operaciones.Introducimos una notación definida como una función que va del género imperativo GI algénero GT correspondiente a su especificación:^: GI GT

    En el mundo de la implementación se preserva la igualdad observacional.

    Comentario:Ver bien esejemplo en hoja 14, no lo ptodo porque está medio raro

  • 8/18/2019 Algo II - Resumen Teóricas

    26/101

    ^nos permite abstraer el elemento del universo del diseño y devuelve el término lógicoal que representa.^ = se explica con (sec)

    Cuando un parámetro se pasa como in/out tengo que decir en la pre que en el estadoinicial ese parámetro vale algo ^p = p0 

    La definición de la representación de un módulo de diseño implica tomar en cuenta todoaspecto referido a cómo se satisfacen los requerimientos declarados en la interfaz:Estructura: describe la estructura interna sobre la cual las operaciones se aplican.Relación entre la representación y la abstracción: Por un lado expone toda restricciónsobre la estructura de representación a fin de que efectivamente pueda ser considerada unaimplementación de un valor del tipo al que implementa; y por otro vincula los valores consu contraparte abstracta, es decir, con algún término de la especificación a quien esterepresente.Algoritmos: eso mismo, la programación en pseudo-código de las operaciones, tanto lasexportadas como las auxiliares, y para todos ellos incluye el cálculo detallado que justificasu complejidad.Servicios usados: declara toda demanda de complejidad, aliasing o efecto colateral que los

    servicios usados de otros tipos en la programación de los algoritmos deban satisfacer

    La estructura de representación describe los valores sobre los cuales se representará elgénero que se está implementando.Esta tiene que tener en cuenta la posibilidad, no solo de ser capaz de representar todos lostérminos del género de la especificación sino también que las operaciones seanimplementables de acuerdo a las exigencias del contexto de uso.Ejemplo:conjuntoDeNat se representa con

    esta tupla vive en el mundo de la implementaciónTodos los conjuntos de naturales pueden representarse con secuencias de naturales el nat eneste caso tiene que ver con que se había pedido el mínimo en O(1)

    Invariante de representación Nos ayuda en la tarea de filtrar toda aquella estructura que no resulta implementar untérmino de la especificación. Expresa de alguna forma un predicado de “sanidad de laestructura (por ejemplo, la secuencia no debe contener números repetidos, y no debecontener valores menores al mínimo).El tema de no tener repetidos en este caso tenía que ver también con un orden decomplejidad pedido.Rep: ê bool

    En el ejemplo del conjunto creamos la operación SinRepetidos que no es relevante en laimplementación si no… ¿?Si pidiera por ejemplo que se representara con secuencias ordenadas, sería posible queexistieran secus no ordenadas que pertenezcan al TAD pero no las puedo representar.SinRepetidos es del mundo de los TADs y hay que axiomatizarla como función

     auxiliar

  • 8/18/2019 Algo II - Resumen Teóricas

    27/101

    El sentido que se le otorga al invariante de representación es que cada operación requiere para su ejecución que el invariante de representación valga sobre las estructuras pasadascomo parámetro y si esto no se cumple, entonces esta asegura que también vale finalizar laejecución. Esta presente en forma implícita en la pre y la post de todas las operacionesexportadas.

    En una operación el estado intermedio “del parámetro” no es visible para quien utiliza

    la aplicación. Acá puede ser que no valga el invariante, pero tengo que restaurarlo alfinal. Debe valer en la pre y en la pos (no importa lo que pase en el medio).

    La función de abstracción es una herramienta que permite vincular una estructura conalgún valor abstracto al que representa. La manera en la que se caracteriza un término es o

     bien a través de los generadores o de los observadores. Normalmente el uso de losobservadores resulta más sencillo.

    Se puede hacer a través de los generadores pero puede traer problemas de

     recursiones infinitas. Mejor hacerlo a partir de los observadores

    Dada una estructura que satisface el invariante de representación me devuelve algún

    conjunto abstracto que está siendo representado por esa estructura.

    C t =obs t’Conjdenat ^ sec conj[nat]

    Interfaz SRCRepresentación abs

    ^

     {1} U ({2} U ({3} U vacio)2, 3, 1

     1 3 2

    El comportamiento de la función de abstracción tiene sentido cuando… ¿?Se caracteriza a partir de los observadores básicos. La función de abstracción tiene quecaracterizar el valor que tienen los observadores básicos sobre la instancia abstracta queestoy caracterizando en función de la estructura.Es una estructura válida porque satisface mi invariante de representación (rep)El triángulo conmuta, esto permite probar que la manera en que se implementó elalgoritmo modifica las estructuras respetando la igualdad observacional.

    Comentario:Vaya uno a…

  • 8/18/2019 Algo II - Resumen Teóricas

    28/101

     Top Down: De los tipos más grandes a los más chicos. A partir del más grande armo laestructura de las cosas necesarias. Desde los que se usan hacia los que son usados.

    Top-down («de arriba abajo») y bottom-up  («de abajo arriba») son estrategias de procesamiento de información

    características de las ciencias de la información, especialmente en lo relativo al software. Por extensión se aplican

    también a otras ciencias sociales y exactas.

    En el modelo top-down se formula un resumen del sistema, sin especificar detalles. Cada parte del sistema se refina

    diseñando con mayor detalle. Cada parte nueva es entonces redefinida, cada vez con mayor detalle, hasta que la

    especificación completa es lo suficientemente detallada para validar el modelo. El modelo top-down se diseña con

    frecuencia con la ayuda de "cajas negras" que hacen más fácil cumplir requisitos aunque estas cajas negras no

    expliquen en detalle los componentes individuales.

    En contraste, en el diseño bottom-up las partes individuales se diseñan con detalle y luego se enlazan para formar

    componentes más grandes, que a su vez se enlazan hasta que se forma el sistema completo. Las estrategias

    basadas en el flujo de información "bottom-up" se antojan potencialmente necesarias y suficientes porque se basan

    en el conocimiento de todas las variables que pueden afectar los elementos del sistema.

    Los métodos top-down fueron favorecidos en la ingeniería de software hasta que llegó la programación orientada a

    objetos a finales de los 1980s.

    El enfoque top-down enfatiza la planificación y conocimiento completo del sistema. Se entiende que la codificación

    no puede comenzar hasta que no se haya alcanzado un nivel de detalle suficiente, al menos en alguna parte del

    sistema. Esto retrasa las pruebas de las unidades funcionales del sistema hasta que gran parte del diseño se ha

    completado.

    Bottom-up hace énfasis en la programación y pruebas tempranas, que pueden comenzar tan pronto se ha

    especificado el primer módulo. Este enfoque tiene el riesgo de programar cosas sin saber como se van a conectar al

    resto del sistema, y esta conexión puede no ser tan fácil como se creyó al comienzo. La reutilización del código es

    uno de los mayores beneficios del enfoque bottom-up.

    El desarrollo de software moderno usualmente combina tanto top-down como bottom-up. Aunque un conocimiento

    completo del sistema se considera usualmente necesario para un buen diseño, haciendo que teóricamente sea un

    enfoque top-down, la mayoría de proyectos de desarrollo de software tratan de usar código existente en algún grado.

    El uso de módulos existentes le dan al diseño un sabor bottom-up. Algunos enfoques usan un enfoque en el que un

    sistema parcialmente funcional es diseñado y programado completamente, y este sistema se va expandiendo para

    llenar los requisitos del proyecto.

    Algoritmos:Servicios usados: operaciones de otros módulos que fueron utilizadas y requerimientos

     sobre estas (complejidad, efectos colaterales, modificación de argumentos…) 

     Algunas otras notas: la asignación por lo general cuando es un tipo básico copia y

     cuando no, da una referencia. Aliasing crear un nuevo nombre para la misma cosa.

     Rep es interno. Se escribe para establecer relaciones concretas entre todas las componentes de la estructura

     ABS: externo. Lo escribimos porque dice cómo se relaciona todo lo que hicimos con

    el lenguaje común (tad)

    Comentario:Ver si se pucompletar con algo

    Comentario: Esto lo chayo no lo decía en ningún la

  • 8/18/2019 Algo II - Resumen Teóricas

    29/101

     La relación TAD modulo no es 1 a 1, distintos módulos pueden explicarse con un

     tad y se puede hacer un módulo de solo una parte de un tad. Más difícil es que un

     módulo se explique con varios tads, pero es posible.

     Hablando de un ejemplo: poner en Sacar() que un elemento no tiene que estar, es

     subimplementar el conjunto, y si bien es válido hay que saber por qué lo estamos

     haciendo y aclararlo (tal vez no queremos que funcione para algunas instancias)

     La notación ^toma las estructura del diseño y habla del tad que representa. El rep vaen el mundo de los tads, no del diseño.

    Cuando voy a hacer el abs debo verificar que la estructura cumpla el rep.

     Las cosas las diferencio con la igualdad observacional, entonces voy a mostrar cómo

     defino los observadores a través de la estructura interna

     La axiomatización tiene que ser clara, no tiene por qué ser eficiente a0 = a no es una pre, pero lo voy a usar después, es una etiqueta de estado

     En las pos, inout siempre tengo que decir si lo cambié o no. El out tengo que decir

    qué asigné en caso de que valga la pre. In no se toca

     El TAD es el ¿qué? Y el diseño el ¿cómo? (que se explica con el tad). El tad es el

     género funcional y el diseño es el genero imperativo.

     Los módulos de abstracción tienen también servicios exportados.

     Encapsulamiento: Publico:

    - Interfaz: operaciones (cómo se llaman, qué reciben, qué devuelven, pre, pos y

     condición de cada una, complejidad, aspectos de aliasing, descripción breve del

     módulo (generos, operaciones))- Se explica con (cual tad) y el TAD completo.

     La operación es imperativa, pero la pre y la pos son funcionales Privado:

    - Estructura de representación- Rep y Abs

    - Algoritmos- Servicios usados: se necesita un módulo x con operaciones y y complejidad z

     Iteradores: El tad iterador me dice qué es y qué hace. Secuencia subyacente: un

    iterador me permite recorrer los elementos de una colección de una manera

    linealizada.

     Aliasing: es una cosa con más de un nombre. El principal error es olvidarse que lo

     hicimos. Cuando hago una asignación de un tipo que no es básico hay aliasing (a no ser que explícitamente lo copie y cree la función: puedo hacer shallow copy, por

    ejemplo de una secuencia, copio la estructura pero apunto a los mismos elementos,

    las secuencias son dependientes. O puedo hacer deep copy, copio también los

    elementos. (VER GRAFICO LINDO EN HOJAS PRACTICA)

  • 8/18/2019 Algo II - Resumen Teóricas

    30/101

    Si devuelvo algo por referencia, aviso que si lo modifica, se modifica también lo de

     adentro.

    Si quiero hacer un puntero a un nodo (en una estructura) rompo el encapsulamiento.

     Puede tener una lista indexada, iterador de la lista, puntero al elemento en sí (pero

     no al nodo)

     El iterador puede ser un puntero al nodo porque forma parte del módulo de la lista

    (es una manera de encapsular el puntero a nodo)

    ÁRBOLES DE BÚSQUEDA

    Alternativas de diseño para diccionarios y conjuntos (son parecidos, así que trabajaremossobre diccionario que es más genérico).

    Diccionario: Agregar, borrar, pertenece, la única diferencia con el conjunto es queobtener me devuelve una definición. Podría pensarlo como un conjunto de tuplas.

    ABM o ABMC* Altas: definir una clave en el diccionario* Bajas: borrado.* Modificaciones: Que puede traducirse en baja+alta* Consultas: Definido? y obtener (ver si está un elemento o consultar su significado).

    Si trabajamos con listas y arreglos tenemos una disyuntiva:* Arreglo ordenado: Búsqueda: O(log n). Inserción/borrado: O(n)* Arreglo no ordenado, listas: Búsqueda O(n). Inserción/Borrado: O(1)

    La alternativa es trabajar con árboles binarios.

    Podemos imaginar al ab representado con punteros, uno al hijo de la izquierda y otro al de

    la derecha.Trabajar con ab nos permite hacer bin() en O(1) pero buscar nos toma O(n).

    La idea es “ser cuidadosos” con la forma que le damos al árbol de modo que al buscarsepamos para qué lado ir.

    Árbol Binario de Búsqueda (ABB):Es un árbol binario con la propiedad de que, para todo nodo, los valores de los elementosen su subárbol izquierdo son menores que el valor del nodo, y los valores de los elementosde su subárbol derecho son mayores que el nodo.Dicho de otra forma: el valor de todos los elementos del subárbol izquierdo es menor que elvalor de la raíz, y el valor de todos los elementos del subárbol derecho es mayor que elvalor de la raíz.Además tanto el subárbol izquierdo como el derecho son también ABB.

  • 8/18/2019 Algo II - Resumen Teóricas

    31/101

    Invariante de representación:EsABB?: ab(α)  bool (rep)EsABB? (a) ≡ nil?(a) ˅ L 

     c: α, está?(c, izq(a)) sii c < clave(raíz(a)) ˄  c: α, está?(c, der(a)) sii c > clave(raíz(a)) ˄ EsABB?(izq(a)) ˄ EsABB?(der(a))

    Todo elemento del subárbol debe ser mayor o menor respectivamente, no alcanza conque solo lo sea la raíz.

    Un árbol degenerado a derecha también puede ser un ABB si es cierto que en cada nodolos elementos que están a la derecha son mayores que la raíz.

    Sin hacer grandes modificaciones puedo agregar ordenado.

    Para la búsqueda recorremos el árbol desde la raíz y en cada paso decidimos si vamos a laizquierda o a la derecha (según el elemento que estamos buscando sea mayor o menor alvalor del nodo).

    Para definir vamos bajando por el árbol hasta llegar a un padre al que le falta un hijo (haciael lado al que deberíamos insertarlo según sea mayor o menor).

    En la interfaz la función se llama definir. Al escribir el algoritmo, la función que tomaun árbol se corresponda con la que había definido en la interfaz: iDefinir (representación del módulo).

    La complejidad es la de la altura del árbol, ya que la acota la distancia entre el nodo y lahoja más lejana.

    Con los ABB entonces:

    Inserción: depende de la distancia del nodo a la raíz, en el peor caso O(n), en el caso promedio (si supongo una distribución uniforme de las claves) sería O(log n).Y lo mismo vale para la búsqueda.

    Borrado:Se contemplan tres casos posibles:- el elemento a borrar es una hoja- el elemento a borrar tiene un solo hijo- el elemento a borrar tiene dos hijos

    El algoritmo básico es muy sencillo: buscamos al padre y eliminamos la hoja. ¡Ojo! No hayforma de “retroceder” en la búsqueda.

  • 8/18/2019 Algo II - Resumen Teóricas

    32/101

    Si el elemento tiene un único hijo h, buscamos al padre y reemplazamos la conexión p->e por la conexión p->h (si e fuera la raíz, lo reemplazamos por h).

    Si el elemento tiene dos hijos podemos pensar que tiene un “predecesor”, que es el máximoelemento menor que e, y sería su antecesor si hiciésemos un inorder del árbol.Llamemos p al predecesor. Este no puede tener dos hijos, porque si no, no sería el

     predecesor inmediato.

    Llamemos h a su único hijo.P es el candidato perfecto para poner en el lugar de e, por lo que ponemos su contenido ahí.Ahora el lugar de p es o bien una hoja, o bien tiene un único hijo (h) y para borrarloestamos en alguno de los casos anteriores.Se podría hacer lo mismo en base al “sucesor” (es decir el mínimo elemento mayor que e)

     Es decir, buscamos el máximo elemento menor que e en su subárbol izquierdo, o el

    mínimo elemento mayor que e en su subárbol derecho.

    Los nodos internos son aquellos que no son ni raíz ni hojas. EL borrado de un nodo internorequiere encontrar al nodo que hay que borrar y a su predecesor inmediato. En el caso peorambos costos son lineales: O(n) + O(n) = O(n)

    Como conclusión, los ABB funcionan razonablemente bien en el caso promedio pero nodan garantías. En el peor caso (y nada impide caer en él) sigue siendo lineal.En este sentido, los arreglos también son lineales en el peor caso y ocupan menos memoria(no hay que guardar punteros).

    Como vimos, en el peor caso todos los algoritmos tienen un costo lineal que en detalle seríaO(h) siendo h la altura del árbol.Si distribuyeramos los nodos de manera “pareja” de manera tal que el árbol tuviese lamínima altura y estuviese siempre parejo ¿qué altura tendría?

    Teorema: un árbol binario perfectamente balanceado de n nodos tiene altura log2 n + 1Supongamos que cada nodo tiene 0 o 2 hijos.

    Llamemos ni a la cantidad de nodos internos (más la raíz) y nh a la cantidad de hojas.

    Propiedad: si n>1, nh = ni + 1

    Demostración:CB: trivial.Suponemos que vale para nh y ni y agregamos un nivel. Las hojas se duplican (n’h = 2nh) ylos nodos internos crecen en tantos como antes tenía hojas:

     N’i = ni + nh = (nh -1) + nh = 2 nh – 1 = n’h – 1Corolario: al menos la mitad de los nodos son hojas.

    Teorema: un árbol binario perfectamente balanceado de n nodos tiene altura |log2 n| + 1Demo (esquema):Sabemos que (1) n = ni + nh (1) y (prop) si n > 1, nh = ni + 1.

  • 8/18/2019 Algo II - Resumen Teóricas

    33/101

    Imaginemos que podamos las hojas: nos queda un árbol con las mismas propiedades, 1menos de altura (llamémosla h), la mitad de los nodos y ahora todas las ramas de la mismalongitud. ¿Cuántas veces más podemos podarlo?Lo podemos pensar al revés: ¿cuántos niveles se pueden agregar desde el comienzo paratener un árbol de altura h?Al agregar un nivel la cantidad de nodos se duplica, porque n’h = n’i + 1, pero n’i = n,entonces n’h = n + 1. Reemplazando en (1) nos queda que n’ = n + (n + 1) + 1.

    Entonces n = 1. 2….2 = 2h = 2 log2 n (porque multiplico por 2 h veces).Por ende h = log2 n y la altura del árbol era h + 1.Detalles de la demo, en el libro.

     Nota: este resultado es generalizable a árboles k-arios

    Si tuviésemos árboles perfectamente balanceados todas nuestras operaciones serían O (logn)Pero es muy costoso mantener el balanceo perfecto. Sin embargo, podemos tener un

     balanceo “casi” perfecto, haciendo que todas las ramas tengan “casi” la misma longitud.“casi” interpretado como: la longitud entre dos ramas cualesquiera de un nodo difiere a losumo en 1.

     Nuestros algoritmos deberán garantizar que sucesiones de inserciones y/o borrados no

    destruyan ese balance (o lo reestablezcan).AVL (Adel’son-Vel’skii & Landis)

    Son árboles “casi” perfectamente balanceados.Resulta mejor tener algo controladamente desordenado que tenerlo perfectamenteordenado.

    Un árbol se dice balanceado en altura si las alturas de los subárboles izquierdo y derecho decada nodo difieren a lo sumo en una unidad.

    Para cada nodo se calcula el factor de balanceo FdB = altura del subárbol derecho – alturadel subárbol izquierdo.En un AVL, para todo n: nodo |FdB(n)| < 1

    El factor de balanceo se calcula en cada nodo y se almacena junto con él.

    El pero AVL (el más desbalanceado) es el árbol de fibonacci.

    Agarramos dos árboles y los concatenamos con una nueva raíz, de modo que no haynuevas hojas y las hojas del nuevo equivalen a la suma de las hojas de los dos anteriores.

    Tenemos el árbol Ph con altura h, P0 el árbol vacío y P1 tiene un solo nodo. Para h >1, Ph tiene una raíz y dos subárboles: Ph-1 y Ph-2 La cantidad de hojas sería la sucesión de fibonacci f h = f h-1 + f h-2La cantidad de nodos es los nodos internos + las hojas, Ph tiene f h + algoComo f h crece exponencialmente con h, eso significa que la altura de Ph que es h crecelogarítmicamente con la cantidad de nodos de P

    Este es el “peor” AVL posible, y aún así su altura es logarítmica en n.

    Comentario:Con tiempodemostración de esto

  • 8/18/2019 Algo II - Resumen Teóricas

    34/101

    De hecho Adel’son-Vel’skii & Landis demostraron que un árbol de fibonacci con n nodostiene altura menor a 1,44 log2 (n+2) – 0,328.

    Por ende un AVL con n nodos tiene altura θ (log n)

     Árbol de Fibonacci

     Árboles de Fibonacci de orden 1, 2, 3 y 4.

    Se llama árbol de Fibonacci a una variante de árbol binario con la propiedad que el orden deun nodo se calcula como la sucesión de Fibonacci.

    El árbol de Fibonacci se define de la siguiente manera:El árbol nulo (no contiene ningún nodo) es de orden 0.

    El árbol que consta de un único nodo es de orden 1.

    Para n > 1, el árbol de Fibonacci de orden n consta de un nodo raíz con el árbol de Fibonacci

    de orden n-1 como hijo izquierdo y el árbol de Fibonacci de orden n-2 como hijo derecho.Dado que este tipo de árbol es un caso particular de un árbol AVL, ya que el factor deequilibrio de todo nodo es -1, un árbol de Fibonacci es balanceado con altura O(log n).En matemáticas, la sucesión de Fibonacci (a veces mal llamada serie de Fibonacci) es la

    siguiente sucesión infinita de números naturales:

    La sucesión comienza con los números 1 y 1,1 y a partir de estos, «cada término es la suma de los dos anteriores»,

    es la relación de recurrencia que la define.Definición recursiva[editar ] Los números de Fibonacci quedan definidos por la ecuación:

    (3)

    partiendo de dos primeros valores predeterminados:

    se obtienen los siguientes números:

     

     

     

     

     

  • 8/18/2019 Algo II - Resumen Teóricas

    35/101

     para

    Esta manera de definir, de hecho considerada algorítmica, es usual en Matemática discreta.

    …Continuando con AVLs

    Con esta estructura, las búsquedas toman O(log n).Para la inserción y el borrado debemos garantizar que los algoritmos restauren el invariantedel AVL.

    Inserción: Insertamos el nuevo nodo como una hoja, como en el ABB, y luegorecalculamos los factores de balanceo que cambiaron por la inserción, solo en la rama en laque ocurrió la inserción, de abajo hacia arriba, ya que no podrían haber cambiado otrosfactores.Si en la rama aparece un factor de balanceo de +-2 hay que rebalancear. Esto se hace através de rotaciones.

    Rotaciones en AVLHay 4 rotaciones posibles, dos simples y dos dobles:

    - RR: inserción en el subárbol derecho R en un hijo derecho R del nodo que se desbalancea.- LR: inserción en el subárbol izquierdo L en un hijo derecho R del nodo que sedesbalancea.- LL: inserción en el subárbol izquierdo L en un hijo izquierdo L del nodo que sedesbalancea.- RL: inserción en el subárbol derecho R en un hijo izquierdo L del nodo que sedesbalancea.

    Ejemplo RR  (LL es simétrica).

    Encontramos el primer nodo de la rama donde cambió el factor de balanceo, en este caso Q.La inserción no influye en los antepasados de P porque luego de la rotación recuperan sufactor de balanceo anterior.

  • 8/18/2019 Algo II - Resumen Teóricas

    36/101

    La complejidad fue O(1) porque solo reacomodamos punteros. Vamos volviendo a laraíz reacomodando todos los factores de balanceo (en el peor de los casos), hasta que

     para algún padre no cambie el FdB.Una vez que hago una rotación, ya está.

    Ejemplo LR (la RL es simétrica)

    Lo que tenemos que hacer es:

    La inserción no influye en la altura de los antepasados de P por lo que no hace falta seguirrebalanceando.

    Al padre de P le cambiamos el hijo por R pero ambos tenían altura h+1+1 entonces ya

    no cambia.

    Si el factor de balanceo de R es del mismo signo alcanza con una rotación simple, si elsigno es distinto entonces tengo una rotación doble.

    Las rotaciones cuestan O(1) pero tengo que encontrar dónde hacerlas, y encontrarlo tomaO(h) = O(log n).

    Entonces el costo de la inserción sería:1) Insertar el nodo: proporcional a la altura del árbol θ (log n)2) Recalcular los FdB de la rama: proporcional a la altura del árbol θ (log n)3) Hacer las rotaciones necesarias: O(1) ya que se hacen una o dos rotaciones por inserción.

    TOTAL: θ (log n)

  • 8/18/2019 Algo II - Resumen Teóricas

    37/101

     Borrado:Borramos el nodo como en un ABB, recalculamos los FdB que cambian por el borrado(solo en la rama del borrado, de abajo hacia arriba).Para cada nodo con FdB +-2 hay que hacer una rotación simple o doble: O(log n)rotaciones en el peor caso.

    Al borrar puedo tener que rotar más de una vez y hay que seguir chequeando

    Eliminemos una hoja de un subárbol izquierdo de P, si el hijo derecho tiene FdB +1

    Eliminemos una hoja de un subárbol izquierdo de P, si el hijo derecho tiene FdB 0

    Eliminemos una hoja de un subárbol izquierdo de P, si el hijo derecho tiene FdB -1 y sunieto también

    Hacemos una rotación entre R y Q (P pasa a +2, R y Q pasan a +1) y luego una rotación P-R.

    Eliminemos una hoja de un subárbol izquierdo de P, si el hijo derecho tiene FdB -1 y sunieto +1

  • 8/18/2019 Algo II - Resumen Teóricas

    38/101

     

    Hacemos una rotación entre R y Q (P y R pasan a +2, Q pasa a 0) y luego una rotación P-R.

    Entonces el costo del borrado sería:1) Borrar el nodo: proporcional a la altura del árbol Θ(log n)2) Recalcular los FdB de la rama: proporcional a la altura del árbol Θ(log n)3) Hacer las rotaciones necesarias: Θ(log n) * Θ(1) ya que en el peor caso hay rotaciones alo largo de toda la rama.

    TOTAL: Θ(log n)

    Tarea:Mirar en detalles las rotaciones, entenderlas y ser capaces de explicarlas.Escribir el pseudo código imperativo de la inserción en AVL y del borrado

    TABLAS DE HASH

    Queremos buscar/obtener en O(1). No siempre se puede pero bastantes veces sí.

    Para algunos tipos particulares de diccionario es fácil, por ejemplo si las claves son nat enun rango k… n+k.

    Agarro un arreglo de n posiciones, pongo las definiciones y puedo buscar en O(1).¿Y si el rango es muy grande pero ralo?

     No tiene sentido hacer un arreglo de 65 millones para guardar 40 DNIs por ejemplo.

    Si relajamos algunos requerimientos podemos empezar a obtener resultados interesantes.Vamos a conformarnos con tener O(1) en el caso esperado sabiendo que podría haber casos

     peores.Supongamos que queremos almacenar n elementos, cuyas claves son valores numéricos de1..N con N >> n. (Por ejemplo 100 DNIs y el significado (β) son todos los datos de la

     persona).

    En lugar de tener un arreglo A[1..N] de β vamos a tener un arreglo A[1…n] de conj< β>. Yademás una función h: DNI [0…n-1]

    Por ejemplo h(d) = d mod 100.Los datos del DNI d están en A[h(d)], pero puede que no sean los únicos.

  • 8/18/2019 Algo II - Resumen Teóricas

    39/101

    Esta es la idea básica de la tabla de hash.H es la función de hash/hashingSi la clave no es numérica alcanza con encontrar una función total de α [0..n-1]

    Entonces: Una tabla de hash es una tupla , donde A es un arreglo de k posiciones y hla función de hash que va de α en [0…k-1].

    La estructura de datos está compuesta por ambas cosas, si cambia h cambia también la

    tabla.

    El invariante y la función de abstracción cambian dependiendo del h en particular.Cada posición del arreglo se suele llamar bucket. Si h distribuye “bien” a las clavestendremos un caso esperado de O(1).El peor caso será bastante mayor y dependerá de cómo resolvamos las colisiones.

    El AVL nunca me da mejor que orden logarítmico, pero tampoco me da peor. En estecaso puedo tener O(1) pero también puedo llegar a tener O(n).

    Se dice que una función de hash es perfecta si  c1,c2 perteneciente a α, h(c1) es distintode h(c2). Para eso necesitamos k > |α|, lo que muy raras veces sucede.

    De hecho suele suceder que |α| >> k. Por ende, es muy probable (más de lo que uno seimagina) que h(c1) = h(c2). Es decir que c1 y c2 colisionen.

    Ejercicio: proponer una función de hash perfecta para el caso en que las claves sean stringsde largo 3 en el alfabeto {a,b,c}

    H puede no ser mala pero tener malos casos en momentos concretos.

    Supongamos que conocemos la distribución de frecuencias de α y llamamos P(c) a la probabilidad de la clave c. Nos gustaría que i perteneciente a [0…k-1], la sumatoria de los c tales que h(c) sea i de los P(c) sea aprox1/k

    Es decir, me fijo todas las claves que hashean a la misma celda y busco que para todas

    la probabilidad sea 1/k (con k = longitud del arreglo).

    Es decir, que para cada posición del arreglo, la probabilidad de que alguna clave vaya a parar ahí sea aproximadamente uniforme. Esto se llama uniformidad simple y es muy difícilde lograr. En gran parte porque P suele ser desconocida.

    A modo de ejemplo, pensemos en A[0…5] y h(c) = c mod 5. Dos conjuntos de datos muysimilares {1,7,10,14} vs {1,6,11,16}, en el primer caso ocupan las posiciones 1,2,0 y 4 y enel segundo quedan todos en la 1.

    Como conocer las distribuciones es muy difícil, lo que se busca en la práctica es tenerindependencia de la distribución de los datos. Y además, saber que las colisiones son

     prácticamente inevitables y habrá que lidiar con ellas sí o sí.

    Comentario: Para pasar Diago 8 Tablas de Hash 1)

  • 8/18/2019 Algo II - Resumen Teóricas

    40/101

    “Paradoja” del cumpleaños: si elegimos 23 personas al azar, la probabilidad de que dos deellas cumplan años el mismo día es mayor a ½ (aprox. 50,7%). Es decir que aúnsuponiendo distribución uniforme de los nacimientos, hay alta probabilidad de festejoconjunto.

    Tarea: pensar en la demo: cuantos pares distintos se pueden generar?

    Corolario: si tenemos una tabla con k = 365 y h distribuye uniformemente entre ellas, aúnasí tenemos probabilidad mayor a ½ de que luego de 23 inserciones se produzca unacolisión.

    Dado que las colisiones sin inevitables, una buena tabla de hash tiene que poder lidiar conellas.Dos familias de métodos:* Hashing (o direccionamiento) abierto: los elementos se guardan en la tabla.* Hashing (o direccionamiento) cerrado: en cada A[i] hay algún tipo de contenedor quealmacena todos los elementos que son hasheados a i.(Ojo! La bibliografía a veces alterna los nombres y las definiciones, lo que importa es laidea más que el nombre).

    Tenemos varias alternativas para el contenedor a usar en un hashing cerrado. Por ejemplo, podrían ser AVLs, pero perderíamos el ansiado O(1).Podrían ser listas, podemos tener una lista que permita agregar el elemento (inserción) enO(1). La búsqueda y borrado sería lineal en la cantidad de elementos del bucket.¿Cuánto pueden medir estas listas?

    Factor de carga:Lo definimos como fc = n/k. Es decir, la relación entre la cantidad de elementos presentes yel tamaño de la tabla.Teorema: Suponiendo que h es simplemente uniforme y se usa hashing cerrado porconcatenación, en promedio:- Una búsqueda fallida requiere θ(1+fc) y una búsqueda exitosa requiere θ(1+ fc/2)Corolario: si n

  • 8/18/2019 Algo II - Resumen Teóricas

    41/101

    1 i = 02 mientras (i < |A| ˄ A[h(c,i)] esté ocupada) incrementar i3 si (i

  • 8/18/2019 Algo II - Resumen Teóricas

    42/101

    Es una forma de evitar la aglomeración primaria, ya que el polinomio varía dependiendodel número de intento.Sin embargo puede producirse aglomeración secundaria: si hay una colisión en el primerintento, sigue habiendo colisiones. (h’(c1) = h’(c2) => h(c1, i) = h(c2, i) )

    Es muy poco probable que dos secuencias de inserción coincidan al principio pero sicoinciden van a coincidir siempre.

    Hashing doble o rehashing: La idea es que el barrido dependa también de la clave.h(c,i) = (h1(c)+i h2(c)) mod |A|, donde h1(c) y h2(c) son dos funciónes de hashing.¿Qué pasa con la aglomeración primaria y la secundaria?

    Es la probabilidad de que la primera coincida y la segunda coincida. La multiplicaciónde dos probabilidades tiende a disminuir. Las funciones tienen que ser independientes(es lo que se busca).

    El hashing doble es muy poco probable que tenga aglomeración primaria y reduce losfenómenos de aglomeración secundaria.

    Si las claves no son naturales, para construir buenas funciones de hash, la idea general esasociar cada clave a un número entero, en una forma que dependerá del contexto. Porejemplo el codo ASCII de cada carácter, multiplicado por cierta base elevada a la posición:

    ascii(prim(s)) . 20 + ascii(prim(resto(s))) . 21 + ....

    Se puede hacer en base 2 o en cualquier otra base.

    Una vez que las claves son numéricas hay varias alternativas que varían en complejidad yen comportamiento con respecto a la aglomeración para terminar de definir la función.

    Basadas en división:

    h(c) = c mod |A| tiene baja complejidad, pero si |A| = 10 p para algún p , todas las clavescuyos últimos dígitos coincidan colisionarían.Y si |A| = 2 p para algún p, lo mismo sucederá con los p bits menos significativos.En general la función de hash debería depender de todas las cifras de la clave, cualquierasea la representación.Una buena elección en la práctica: un número primo no demasiado cercano a una potenciade 2 (por ejemplo h(c) = c mod 701 para |A| = 2048 valores posibles)

     No suele ser buena idea que la función de hash dependa solo de algunos dígitos.Hay que tratar de que el tamaño de la tabla no sea potencia de 2, ni de nadie (tratar de

     buscar números primos.

    Bits más y menos significativos 

  • 8/18/2019 Algo II - Resumen Teóricas

    43/101

    Un conjunto de bits, como por ejemplo un byte, representa un conjunto de elementos ordenados. Se llama bit más

    significativo (MSB) al bit que tiene un mayor peso (mayor valor) dentro del conjunto, análogamente, se llama bit

    menos significativo (LSB) al bit que tiene un menor peso dentro del conjunto.

    En un Byte, el bit más significativo es el de la posición 7, y el menos significativo es el de la posición 0

    +---+---+---+---+---+---+---+---+

    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ← Posición del bit

    +---+---+---+---+---+---+---+---+

    |128|64 |32 |16 | 8 | 4 | 2 | 1 | ← Valor del bit de acuerdo a su posición 

    +---+---+---+---+---+---+---+---+

    ↖ Bit más significativo ↖ Bit menos significativo

    En una palabra de 16 bits, el bit más significativo es el de la posición 15 y el menos significativo el de la posición 0.

    Tomemos, por ejemplo, el número decimal 27 codificado en forma binaria en un octeto:

    -> 0 0 0 1 1 0 1 1

     Aquí, el primer '0', el de la izquierda, (que se corresponde con el coeficiente de ), es el bit más significativo,

    siendo el último '1', el de la derecha, (que se corresponde con el coeficiente de ), el menos significativo.

    En cualquier caso, el bit más significativo es el del extremo izquierdo y el menos significativo el del extremo derecho.

    Esto es análogo al sistema decimal, en donde el dígito más significativo es el de la izquierda y el menos significativo

    el de la derecha, como por ejemplo, en el número 179, el dígito más significativo, el que tiene mayor valor, es el 1,(el de las centenas), y el menos significativo, el 9, (el de las unidades).

    Este concepto de significatividad  se extiende al conjunto de bytes que representan números o valores numéricos

    Byte (B)1 2 (pronunciada [bait] o ['bi.te]) es una unidad de información utilizada como un múltiplo del bit.

    Generalmente equivale a 8 bits 

    Partición y extracción:Pueden ser útiles cuando la clave es muy larga, la idea es pensar a c como compuesto porsegmentos c1, c2, …, cnEn el caso de partición se busca calcular: h(c) = h’(c1, c2, …, cn)Ejemplo, a partir del número de la tarjeta de crédito en cuatro partes y luego hacer (c1 + c2 + c3 + c4) mod 701.En el caso de extracción, la idea es quedarse solo con algunos de los ci.

    Entonces hay que tratar de que las funciones de hash no sean lineales y hacerlas lo máscomplejas posible, siempre que garantice que recorra toda la tabla.

    Las funciones de hash tienen interés más allá del mundo de las estructuras de datos. Se usanmucho en seguridad. En cripto se utilizan hashes de una vía, es decir, la idea es que sea

     prácticamente imposible obtener la preimagen.Se suele pedir que cumplan con:

    * Resistencia a la preimagen: dado h debería ser difícil encontrar un m tal que h =hash(m).

    * Resistencia a la segunda preimagen: dado m1 debería ser difícil encontrar un m2distinto de m1 tal que hash(m1) = hash(m2).

    Etc…Son muy útiles para almacenar contraseñas (se encriptan en las bases de datos), ya queconviene que no se puedan leer, y para asegurar que el software bajado es el correcto.

  • 8/18/2019 Algo II - Resumen Teóricas

    44/101

    NOTA: chusmear http://es.wikipedia.org/wiki/MD5 MD5 y SHA-1FALTO PASAR EL BONUS Y UNIVERSAL HASHING (3 últimas diapos de Teorica2 de Hashing que en realidad son extras)

    BUSQUEDA DIGITAL, TRIES, ETC.

    Motivación:

    Queremos que el tiempo sea menos dependiente de la cantidad de claves.Rendimiento razonable en el peor casoRápidos en la prácticaAdecuados para claves de tamaño variableAdecuados para otro tipo de aplicaciones (por ejemplo pattern matching, indexación, engeneral “text retrieval”, compresión de textos…)

    Pattern Matching es el concepto asociado al chequeo estructural de un dato respecto de una estructura esperada.

    Hacemos análisis asintótico en el peor de los casos (en función del tamaño del input). No se puede buscar en un tiempo menor que log n con ningún algoritmo.Si tengo un conjunto de n cosas necesito log n bits (una respuesta si/no es 1 bit).Pattern matching: detecta la presencia de un patrón de texto en un texto más grande.

    Indexación: precomputar el pattern matching de manera fácil de ejecutar.Text retrieval: recuperación de la información

    Idea: No hacer comparaciones de claves completas, sino partes de ellas.Requiere poder hacer operaciones sobre esas partes. Por ejemplo, si las claves son strings,vamos a trabajar con caracteres. Si las claves son enteros, vamos a trabajar con dígitos o

     bits.

    Presuponemos que mirar una clave es más costoso que mirar un “cachito”.

    Desventajas: Algunas implementaciones pueden requerir mucha memoria.Las operaciones sobre las componentes de las claves puede no ser fácil, o ser muyineficiente en algunos lenguajes de alto nivel.

    Es decir, no siempre es verdad que nuestra presunción sea verdadera, depende dealgunos lenguajes