Universidad Simón Bolívar Departamento de Computación y Tecnología de la Información CI2527 Estructuras Discretas III SeptiembreDiciembre 2014 Práctica - Semana 06 Homomorsmos, Isomorsmos y Grupos Cíclicos Continuación. En esta práctica continuaremos practicando los conceptos asociados a homomorsmos, isomorsmos y grupos cíclicos. 0. ¾Qué se ha visto hasta ahora en la teoría? • Teorema de Lagrange. • Homomorsmos e Isomorsmos. • Grupos Cíclicos. 1. Implementemos las ideas. Haskell está centrado en dos conceptos principales. En primer lugar, las funciones. Pero estas funciones se hacen interesantes es a través de un muy completo sistema de tipos. Los tipos juegan un papel protagónico al usar un lenguaje como Haskell y el mismo tiene muchas facilidades para la denición de nuevos tipos (en el Laboratorio de Lenguajes de Programación I se explorarán a mayor profundidad). Consideremos la siguiente denición para un predicado de dos argumentos que decide si el primero es menor o igual que el segundo. pred_menor_o_igual x y = x <= y ¾Cómo sería la rma de este predicado? Es tentado decir que es pred_menor_o_igual :: a -> a -> Bool, pero tal rma no estaría completa y, si la agregamos al predicado, Haskellno aceptará el programa. ¾Entonces, qué le falta? El predicado funciona sobre cualquier tipo, si, pero siempre y cuando ese tipo soporte la operación <=. Por lo tanto, la rma del predicado se completa de la siguiente forma: pred_menor_o_igual :: (Ord a) => a -> a -> Bool 1 1.1. Programando con clase. La forma en que se le dijo a Haskell que es posible aplicar <= sobre los elementos fue agregando el prólogo Ord a a la rma de la función. A Ord y otros similares como Eq (para comparaciones por igualdad), Show (para imprimir por pantalla), Bounded (para mínimo y máximo), etc., se les conoce como clases de tipos. Además de las que vienen con Haskell, es posible crear clases de tipos propias. Estas clases sólo proponen la rma de las funciones que todo tipo de esa clase debe implementar (aunque es posible dar deniciones por defecto de las mismas). Como ejemplo, creemos una clase de tipos que soporten elevar al cuadrado: class Duplicable a where cuadrado :: a -> a Nótese que sólo se especicó la rma de cuadrado y no se dio una implementación. ¾Cómo agrego miembros a esta clase de tipos? Haciendo instancias. Esto es, diciendo cómo se comporta la función cuadrado para el tipo deseado. Por ejemplo, agreguemos a los enteros: instance Duplicable Integer where cuadrado = (^2) ½Listo! Hagamos ahora lo mismo con listas genéricas. instance Duplicable [a] where cuadrado x = x ++ x Ejercicios: 1. Realice una instancia de la clase Duplicable para booleanos, tal que el resultado siempre sea True. 2. Construya una clase de tipos Restable que soporte la función resta (toma el primer argumento y le quita el segundo). (a) Realice una instancia de la clase Restable para enteros. (b) Realiza una instancia de la clase Restable para listas (que se comporte como la resta de conjuntos, conservando el orden). 2 1.2. Grupoides, Semigrupos, Monoides y Grupos, reloaded. Las rmas algebraicas en realidad tratan de dar estructura y propiedades a un conjunto de fondo, llamado carrier. En prácticas pasadas ya hemos visto que la noción de un tipo es similar a la de un conjunto. Por lo tanto, podemos denir estructuras algebraicas sobre tipos. ½Eso lo hace ideal para Haskell! Podemos usar las clase de tipos para darle la estructura que queremos a estos tipos. Un grupoide es un conjunto (o tipo) base, junto con un operador binario cerrado. Podemos representar este tipo de estructura en Haskell con una clase de tipos: Grupoides: class Grupoide c where (<+>) :: c -> c -> c Creamos un operador <+> que sólo por su rma ya es cerrado en el tipo c. A continuación podemos empezar a agregar tipos a esta clase. Por ejemplo, podemos agregar al tipo entero: instance Grupoide Integer where (<+>) = (+) Hemos agregado a los enteros un operador <+> que se comporta como la suma. Así, los enteros y este operador vienen a representar el grupoide aditivo de los enteros. Un semigrupo es un Grupoide donde se exige que el operador sea asociativo. Para que un tipo pueda ser aumentado a un semigrupo, este debe ser un grupoide y además soportar comparaciones por igualdad (con el objetivo de comprobar la propiedad asociativa). Semigrupos: Podemos representar este tipo de estructura en Haskell con una clase de tipos: class (Grupoide c, Eq c) => Semigrupo c Aquí ocurrieron dos cosas diferentes: • Creamos una clase de tipos condicionada. Al igual que para las funciones, en la que se coloca un prólogo a la rma, se puede colocar un prólogo a la denición de la clase. 3 • No necesitamos incluir el where, ya que la rma de la estructura algebraica entre grupoides y semigrupos no cambia. Lo que diferencia uno del otro es la propiedad asociativa del operador. Podemos ahora implementar una propiedad sobre tipos que sean semigrupos que verique la asociatividad del operador: prop_asociativa :: (Semigrupo c) => c -> c -> c -> Bool prop_asociativa a b c = a <+> (b <+> c) == (a <+> b) <+> c Nótese que como se agregó Grupoide c como prólogo de la rma, la operación <+> se hizo disponible. Para incluir a los enteros en la clase Semigrupo, basta con expresarlo directamente. Esto, ya que los enteros ya son miembros de Grupoide y Eq. instance Semigrupo Integer Un monoide es un semigrupo, en donde uno de los elementos del carrier es considerado neutro o identidad. Monoides: Podemos representar este tipo de estructura en Haskell con una clase de tipos: class (Semigrupo c) => Monoide c where e :: c Podemos ahora implementar una propiedad sobre tipos que sean monoides que verique que el elemento escogido es en verdad neutro: prop_neutro :: (Monoide c) => c -> Bool prop_neutro a = a <+> e == a && e <+> a == a Para incluir a los enteros en la clase Monoide se debe decir quién es su elemento neutro (con respecto a la suma, que fue como denimos su grupoide). instance Monoide Integer where e = 0 4 Un grupo es un monoide en el que cada elemento del carrier tiene un inverso. Esto es, existe una función que toma cada elemento del carrier y lo lleva a algún elemento que sea su inverso. Grupos: Podemos representar este tipo de estructura en Haskell con una clase de tipos: class (Monoide c) => Grupo c where inv :: c -> c Podemos ahora implementar una propiedad sobre tipos que sean grupos que verique que la función construya inversos: prop_inverso :: (Grupo c) => c -> Bool prop_inverso a = a <+> (inv a) == e && (inv a) <+> a == a Para incluir a los enteros en la clase Grupo se debe decir quién es su función para inversos (con respecto a la suma, que fue como denimos su grupoide). instance Grupo Integer where inv = negate La función negate toma un entero y produce el negativo del mismo. Ejercicios: 1. Considere el grupo hB, ∨, f alse, ¬i, de los booleanos con la disyunción, elemento neutro f alse y la negación como inverso. (a) Incluya a los booleanos como miembros de Grupoide con operador ∨. (b) Sobre eso, incluya a los booleanos como miembros de Semigrupo. (c) Sobre eso, incluya a los booleanos como miembros de Monoide, con elemento neutro f alse. (d) Sobre eso, incluya a los booleanos como miembros de Grupo, con ¬ como inverso. 2. Considere el monoide hString, ++, ""i, de las cadenas de caracteres con la concatenación y la cadena vacía como elemento neutro. (a) Incluya a las cadenas de caracteres como miembros de Grupoide con operador ++. (b) Sobre eso, incluya a las cadenas de caracteres como miembros de Semigrupo. 5 (c) Sobre eso, incluya a las cadenas de caracteres como miembros de Monoide, con la cadena vacía como elemento neutro. (d) ¾Es posible aumentar esta estructura tal que sea un grupo? ¾Cómo sería el inverso de ser posible? 1.3. ¾Y las tablas? Muchas veces es deseable tener una tabla de operadores explícita, sobre elementos abstractos como a, b, c, etc. Esto también es posible de construir en Haskell y veremos a continuación el camino. Supongamos que queremos implementar el grupoide hG, ⊕i, que tiene G = {a, b, c, d} como carrier y el operador ⊕ está denido por la siguiente tabla: ⊕ a b c d a a b c d b b c d a c c d a b d d a b c En primer lugar, debemos crear un tipo que represente al conjunto G. Eso lo podemos hacer a través de una denición de datos: data G = A | B | C | D Además, es conveniente poder imprimir miembros de este tipo y poder compararlos por igualdad. Para este tipo de clases, Haskell trae implementaciones por defecto. Para utilizarlas, basta con agregar deriving y las clases que se desea instanciar. data G = A | B | C | D deriving (Eq, Show) Crear la tabla de operadores es verboso, en el sentido que hay que escribir bastante (contrario a lo que hemos visto en Haskell). Existen maneras de escribir estas tablas de una forma más compacta, pero hace falta ahondar mucho más en el lenguaje para utilizarlas. instance Grupoide G where A <+> A = A; A <+> B = B; B <+> A = B; B <+> B = C; C <+> A = C; C <+> B = D; D <+> A = D; D <+> B = A; A B C D <+> <+> <+> <+> C C C C = = = = C; D; A; B; A B C D <+> <+> <+> <+> D D D D = = = = D A B C Nota: El punto y coma que aparece es una manera sintáctica de hacerle creer a Haskell que se ha saltado de linea. De esa forma pudimos escribir la función en forma de tabla y no como 16 las. 6 Ejercicios: 1. Incluya a G como miembro de la clase Semigrupo. 2. Sobre eso, incluya a G como miembro de la clase Monoide (debe averiguar el neutro). 3. Sobre eso, incluya a G como miembro de la clase Grupo (debe averiguar los inversos). 2. Ejercicios de la práctica. 1. Dado el grupo G = hZ12 , ⊕12 , 0, (a)−1 i donde ⊕12 está denido por: a ⊕12 b = a + b (a) Dé el orden de todos los posibles subgrupos de G. (b) Para el subgrupo no trivial de menor tamaño halle las clases laterales de ese subgrupo en G. 2. Sea h un homomorsmo de un grupo hG, ∗, e, ()−1 i en un grupo hG0 , ∗0 , e0 , ()−1 i. Demuestre: 0 (a) Si e es la identidad de G entonces h.e es la identidad de G0 . (b) Si a ∈ G entonces h(a−1 ) = (h.a)−1 es la inversa en G0 . (c) Si hH, ∗, e, ()−1 i es un subgrupo de hG, ∗, e, ()−1 i, entonces h(H) es un subgrupo de 0 hG0 , ∗0 , e0 , ()−1 i 0 0 (d) Si hK 0 , ∗0 , e0 , ()−1 i es un subgrupo de hG0 , ∗0 , e0 , ()−1 i, entonces h−1 (K 0 ) es un subgrupo de hG, ∗, e, ()−1 i. 3. Diga si hay o no un isomorsmo de G = hZ, +, 0, −ai en G0 = hR, +, 0, −ai. Justique su respuesta. 4. Sea h una función de hZ, +, 0, −ai en h3Z, +, 0, −ai denida por h.n = 3n. Diga si h es un isomorsmo. (3Z es el conjunto de todos los múltiplos de 3). 5. ¾Es el grupo hZ, +, 0, −ai isomorfo al grupo hQ, +, 0, −ai? 6. Si hG, ∗, ei es un grupo cíclico, demuestre que todo subgrupo de hG, ∗, ei es cíclico. 7. Dado el grupo G = hZ24 , ⊕24 , 0, ()−1 i, para los grupos H = h4i y K = h8i, halle las clases laterales de H en G y de K en G. 3. Lecturas adicionales: • Capítulo 4 de las guías del prof. Vicente Yriarte. • Capítulo 3 de Learn You A Haskell.. 4. Tarea: • Completar los ejercicios y demostraciones que no se hayan hecho en clase. • Descargar el archivo semana06.hs y completar los ejercicios faltantes. Ricardo Monascal / SeptiembreDiciembre 2014 7