TEORÍA DE ALGORITMOS 1 – 75.29 TRABAJO PRÁCTICO Nº: 1 INTEGRANTES Padrón 84672 PARA USO DE LA CÁTEDRA Primera entrega Corrector Observaciones Segunda entrega Corrector Observaciones Nombre y Apellido Juan Martín Muñoz Facorro Email [email protected] ÍNDICE Enunciado ........................................................................................................................................................... 3 Ejemplo .......................................................................................................................................................... 3 Algoritmo de Gale & Shapley ......................................................................................................................... 3 Datos de entrada............................................................................................................................................ 4 Resolución .......................................................................................................................................................... 5 Backtracking ................................................................................................................................................... 5 Orden del algoritmo ................................................................................................................................... 5 Gale & Shapley ............................................................................................................................................... 6 Orden del algoritmo ................................................................................................................................... 6 Conclusiones ....................................................................................................................................................... 8 Apéndice A ......................................................................................................................................................... 9 Apéndice B ........................................................................................................................................................ 11 Apéndice C ........................................................................................................................................................ 13 2 ENUNCIADO Ustedes han sido contratados como casamenteros de una comunidad muy cerrada, formada por N hombres y N mujeres, todos solteros, y que deben casarse (todos) entre sí este año. Basarán su asignación en las preferencias de cada uno (es decir que le pedirán a cada hombre que haga un ranking de todas las mujeres, calificándolas de 1 a N, y de igual modo le pedirán a cada mujer que haga un ranking de todos los hombres). Para mantener la buena fama de casamenteros que tienen (que los ha hecho muy solicitados por diversas comunidades) deben evitar que los matrimonios se separen al poco tiempo. En particular quieren evitar armar parejas inestables (decimos que estamos en una situación inestable si un hombre y una mujer que están casados con otros preferirían estar casados entre sí). El problema de armar las parejas sin que haya una situación inestable se conoce como el problema de los matrimonios estables. Trabajaremos con los siguientes conceptos: Un par hombre-mujer es un par bloqueante de la asignación si están en pareja con otros cuando preferirían estar en pareja entre sí. Una asignación es estable si no contiene ningún par bloqueante (y es inestable si contiene alguno). EJEMPLO Hombres Mujeres a b c d J K L M 1° J M M K a d a c 2° K L L J c b d d 3° L J J L b c b a 4° M K K M d a c b Figura 1: Preferencias de hombres y mujeres Si se asignara (a, L), (b, J), (c, M), (d, K) entonces (a, J) resultaría un par bloqueante (y por lo tanto (a, L), (b, J), (c, M), (d, K) resultaría una asignación inestable, en la cual a y J terminarían separándose de sus respectivas parejas para casarse entre sí, ya que cada uno de ellos estaría casado con otro/a pese a que a eligió a J en primer lugar y J eligió a a en primer lugar. En cambio (a, J), (b, L), (c, M), (d, K) es una asignación estable. Una manera de realizar una asignación estable es por backtracking. Sin embargo, existe un algoritmo (de Gale & Shapley, 1962) que es más eficiente, ya que tiene orden polinomial en la cantidad de personas. ALGORITMO DE GALE & SHAPLEY El algoritmo procede por rondas: En la primera ronda cada hombre se le declara a la mujer que pre_ere independientemente de que algún otro se le haya declarado. Entre todas las propuestas que recibió, cada mujer elige al hombre 3 mejor posicionado en su ranking y se compromete con él. Si una mujer no recibe ninguna propuesta, espera hasta la próxima ronda. En cada ronda subsiguiente los hombres que ya estén comprometidos no hacen nada. Cada hombre no comprometido se le declara a la mujer mejor posicionada en su ranking que aún no lo rechazó, independientemente de que ella esté comprometida o no. Cuando le toca el turno a las mujeres, cada mujer acepta la propuesta del hombre mejor posicionado en su ranking (incluso puede llegar a romper un compromiso; también puede suceder que su novio actual esté mejor posicionado que todos sus nuevos pretendientes, en cuyo caso se queda con el novio actual). Si una mujer no recibe ninguna propuesta, espera hasta la próxima ronda. Mientras queden hombres no comprometidos al final de una ronda, se hace una nueva ronda. Se pide: 1. 2. Resolver el problema por backtracking. a. Calcular el orden. Programar el algoritmo de Gale & Shapley para resolver el problema. a. Elegir las estructuras de datos adecuadas para implantar eficientemente el algoritmo y calcular el orden. Justificar. b. Justificar que la asignación es completa y estable, es decir que el algoritmo no deja personas solteras y la asignación realizada es estable. c. Justificar que el algoritmo termina siempre. d. ¿El algoritmo es simétrico? Justificar. e. Si se les permitiera a las mujeres cambiar la lista de preferencia durante la ejecución del algoritmo, como podría hacer una mujer dada, para conseguir el hombre óptimo (Pista: podría mentir en su lista de preferencias, hasta conseguir que el hombre óptimo se le declare). f. Explicar de qué estrategia algorítmica se trata. Justificar. DATOS DE ENTRADA El programa deberá recibir por la línea de comando el nombre de un archivo de texto con el siguiente formato: Alberto: Ana,Betina,Carla,Dalma Bato: Dalma,Carla,Betina,Ana Carlos: Dalma,Carla,Betina,Ana Demian: Betina,Ana,Carla,Dalma [línea en blanco] Ana: Alberto,Carlos,Bato,Demian Betina: Demian,Bato,Carlos,Alberto Carla: Alberto,Demian,Bato,Carlos Dalma: Carlos,Demian,Alberto,Bato Donde la primera persona que aparece en la lista de cada participante es la más preferida y así sucesivamente hasta llegar a la menos preferida en último lugar. 4 RESOLUCIÓN BACKTRACKING La solución por medio de este método recorre todas las combinaciones de parejas posibles, evaluando si la asignación de parejas encontrada es estable, si lo es, se encontró una posible solución, si no lo es, se sigue formando las parejas aún no combinadas. Para la implementación del algoritmo se precisa saber qué parejas se han formado así como también qué hombres y qué mujeres ya tienen una pareja asociada. También se debe realizar, con la creación de cada nueva pareja, la verificación de que hasta el momento la solución es estable, dado que no tiene sentido seguir formando parejas si un par de ellas presenta una inestabilidad. La lista de parejas de la solución se implementó con una lista doble, más específicamente, la estructura de datos collections.deque de python. Los acceso al principio y fin de la lista son de orden O(1)1. Para mantener la lista de hombres se utilizó la misma estructura antes mencionada, con la diferencia que su utilización fue sólo a modo de pila. La lista de mujeres se implementó con una lista de python, la cual es en realidad un arreglo que se redimensiona según la cantidad de ítems que contiene. 2 Para lograr un orden constante al determinar si una persona prefiere a otra, cada una de ellas tiene una tabla de hash con los nombres del sexo opuesto como clave y la preferencia como valor. La estructura utilizada para el hash es la obtenida por medio de la función dict, la cual tiene complejidad promedio O(1) al agregar o acceder a un elemento. En el Apéndice A se puede encontrar el código fuente de la clase Person utilizada para la resolución tanto por backtracking como por el agoritmos de Gale y Shapley. El Apéndice B contiene el código específico para la resolución por el método de backtracking, es decir, la clase Solution y Backtracking. ORDEN DEL ALGORITMO Dado que este algoritmo prueba todas las combinaciones de parejas posibles se puede pensar el recorrido del espacio de soluciones como el representado por el siguiente árbol: 1 Time Complexity - http://wiki.python.org/moin/TimeComplexity 2 Python list implementation - http://www.laurentluce.com/posts/python-list-implementation/ 5 Siguiendo este árbol puede determinarse que si en cada rama se tienen en cuenta solo las mujeres solteras para cada hombre se probarán combinaciones del orden de O(n!). Dado que en el algoritmo implementado se recorren todas las mujeres para buscar la primera soltera, el algoritmo es en realidad de orden O(nn), lo cual a fines prácticos es tan desventajoso como la función factorial. GALE & SHAPLEY El algoritmo de esta sección no necesita mantener la lista de pares encontrados, dado que a medida que se van formando y modificando las parejas, la persona con la que quede finalmente comprometida será la definitiva. Para mantener la lista de hombres solteros se utiliza la lista doble collections.deque. La lista de preferencia para cada persona se carga en una tabla de hash de la misma forma que en el algoritmo por backtracking. Dado que se debe acceder a cada mujer según la preferencia de cada hombre dependiendo del orden de cada hombre, la lista de mujeres se implementó con una tabla de hash, con lo cual se puede acceder por nombre a cada una de ellas con orden O(1). ORDEN DEL ALGORITMO Considerando que cada hombre en el peor de los casos se le declara a cada mujer de su lista de preferencias, la complejidad del algoritmo de Gale y Shapley es O(n2). El código fuente de la implementación del algoritmo se puede encontrar en el Apéndice C. Asignación Completa y Estable Para probar que ningún hombre o mujer queda sin pareja, podemos plantear que si al final de la corrida del algoritmo un hombre se le declaró a cada mujer de su lista y sigue soltero, entonces cada una de esas mujeres prefirió a otro hombre antes que a él, por lo cual cada una de ellas debería estar en pareja. Como hay tantas mujeres como hombres esto es imposible, por lo que se puede concluir que todo hombre al final del algoritmo tiene pareja. La estabilidad de la asignación surge del siguiente planteo: la asignación es inestable si al finalizar el algoritmo dado un hombre m comprometido con una mujer w, y otro hombre m’ comprometido con otra mujer w’; m prefiere a w’ antes que a w y w’ prefiera a m antes que a m’. Si ocurre esto, significa que m se le tiene que haber declarado antes a w’ que a w, dado que se encuentra antes en la lista de preferencia, pero si ocurrió esto, y w’ prefiera a m, entonces nunca podría haber abandonado a m por m’. Concluimos entonces que la inestabilidad planteada no puede existir. El Algoritmo Termina Siempre Como consecuencia de lo expuesto en el punto anterior (ningún hombre queda sin pareja), es válido afirmar que el algoritmo termina, dado que si ningún hombre queda sin pareja, lo mismo pasa con las mujeres. Simetría 6 El algoritmo de Gale y Shapley tiene la característica que para el género que se declara al otro, la persona con la que se compromete, si esta cambia durante la corrida, va decreciendo en el orden de preferencia. Mientras que para el género al cual se le propone, la persona con la que se va comprometiendo, se encuentra cada vez más arriba de su lista de preferencia. Por lo descrito anteriormente, el algoritmo no es simétrico ya que la situación de los dos géneros no es equivalente. Por ejemplo, si los hombres son los que se declaran, terminarán comprometidos con la mejor mujer posible que no los rechazó o abandonó por otro, mientras que las mujeres quedarán comprometidas con el mejor hombre que se les declaró. Manipulación de Preferencias Un miembro del género al que se le declaran, puede lograr obtener a la primera persona de su lista de preferencias, mintiendo al respecto de esta misma, siguiendo la siguiente determinación cada vez que se le declara una persona: aceptar cualquier declaración mientras que la pareja que tiene no sea la persona óptima de su lista de preferencia. Para probar que esto funciona, podemos suponer el caso natural (determinado por las preferencias de la instancia del problema) en el cual a una persona se le declaran todas las personas del otro género. En este caso, la persona recibe las propuestas de sus pretendientes de menor a mayor en relación a su preferencia, pero finalmente terminará con la primera y óptima. Ahora en vez de que la situación se dé naturalmente suponemos que la persona miente, conociendo la mecánica de la selección de parejas. Esto no cambia el escenario planteado en el caso natural, dado que la persona aceptará tantas personas como propuestas reciba, y en nuestro caso, sólo hasta que encuentre a la persona que busca. Estrategia Algorítmica El algoritmo responde a una estrategia en la cual en cada iteración se trata de formar una pareja, teniendo en cuenta sólo los atributos de dos elementos del conjunto, lo que demuestra una visión y un intento de optimización pura y exclusivamente local. Cada iteración posterior trabajará de la misma forma, pero teniendo en cuenta el escenario heredado de otras iteraciones, construyendo incrementalmente una solución final. Las características del algoritmo de Gale y Shapley mencionadas determinan que la estrategia utilizada es greedy. 7 CONCLUSIONES Luego de implementar los dos algoritmos presentes en este informe, se obtuvo una visión más detallada de los diferentes aspectos a tener en cuenta al seleccionar las estructuras de datos a utilizar, así como la necesidad de verificar el orden de las estructuras implementadas por terceros, para garantizar que la complejidad que se busca pueda lograrse. La diferencia de los órdenes entre los dos algoritmos analizados es abismal. El de Gale y Shapley, aunque no sea simétrico y, como consecuencia, favorezca a uno u otro género, logra obtener una solución en tiempo polinómico, lo cual es altamente deseable, sobre todo si la alternativa disponible, algoritmo de backtracking, presenta un orden de tiempo exponencial. 8 APÉNDICE A PERSON.PY # -*- coding: utf-8 -*from collections import deque class Person: prefs = None prefnames = deque() fiancee = None name = None def __init__(self, name, preferences=[]): """Initializes the preferences hashtable. O(n)""" self.name = name self.prefs = dict() i = 0 for name in preferences.split(","): name = name.strip() self.prefs[name] = i self.prefnames.appendleft(name) i = i + 1 def prefers(self, p1): """Determins wheter this person prefers its current fiancee or the one specified in the parameter. O(1)""" result = True if self.fiancee is not None: result = self.prefs[p1.name] > self.prefs[self.fiancee.name] return result def engage(self, p): self.fiancee = p p.fiancee = self def break_up(self): if self.fiancee is not None: self.fiancee.fiancee = None self.fiancee = None def get_prospect(self): if len(self.prefnames) > 0: return self.prefnames.pop() return None 9 MAIN.PY # -*- coding: utf-8 -*############################################################################### # Import Modules ############################################################################### import sys from backtracking import backtracking from galeshapley import galeshapley from collections import deque filename = sys.argv[1] print "------------------------------------" print "Backtracking" b = backtracking.Backtracking(filename) b.match() print "------------------------------------" print "Gale & Shapley" print "---------------------" gs = galeshapley.GaleShapley(filename) gs.match() 10 APÉNDICE B SOLUTION.PY # -*- coding: utf-8 -*from collections import deque class Solution: __pairs = None def __init__(self): self.__pairs = deque() def is_stable(self, pair): for p in self.__pairs: m1 = p[0] w1 = p[1] m2 = pair[0] w2 = pair[1] if ((m1.prefers(w2) and w2.prefers(m1)) or (m2.prefers(w1) and w1.prefers(m2))): return False return True def push(self, pair): self.__pairs.append(pair) def pop(self): pair = self.__pairs.pop() return pair def show(self): print "----------------------" for p in self.__pairs: print "(", p[0].name, ", ", p[1].name, ")" BACKTRACKING.PY # -*- coding: utf-8 -*from collections import deque from model.person import Person from model.solution import Solution class Backtracking: men = deque() women = [] solution = Solution() def __init__(self, filename): self.load_data(filename) def load_data(self, filename): """Loads men and women [O(n)] and their preference lists [O(n)] O(n^2)""" file = open(filename) genre = 'Male' for line in file: words = line.split(":") if line == '\n': genre = 'Female' else: 11 name = words[0] preferences = words[1] person = Person(name, preferences) if genre == 'Male': self.men.append(person) else: self.women.append(person) file.close() def match(self): """Finds all stable matchings O(n^n)""" # If there are still if len(self.men) != 0: #Take the first man in the list m = self.men.pop() #Check for women without commitment for w in self.women: if w.fiancee is None: #Build pair pair = [m, w] m.engage(w) #Verify that new pair doesn't create instability if self.solution.is_stable(pair): self.solution.push(pair) self.match() self.solution.pop() m.break_up() # Get the man back in the stack self.men.append(m) else: self.solution.show() # Solution found 12 APÉNDICE C GALESHAPLEY.PY # -*- coding: utf-8 -*from collections import deque from model.person import Person from model.solution import Solution class GaleShapley: singles = deque() men = [] women = dict() def __init__(self, filename): self.load_data(filename) def load_data(self, filename): """Loads men and women [O(n)] and their preference lists [O(n)] O(n^2)""" file = open(filename) genre = 'Male' for line in file: words = line.split(":") if line == '\n': genre = 'Female' else: name = words[0] preferences = words[1] person = Person(name, preferences) if genre == 'Male': self.men.append(person) else: self.women[name] = person file.close() self.singles = deque(self.men) def match(self): """Finds a stable matching in O(n^2)""" # If there are still single men while len(self.singles) != 0: #Take a single man m = self.singles.pop() #Get next woman's name in the preference list name = m.get_prospect() if name is None: raise Exception("This shouldn't be happening!") #Get woman by name w = self.women[name] #Check who the woman prefers if w.prefers(m): w.break_up() m.engage(w) else: self.singles.appendleft(m) for m in self.men: print "(", m.name, ",", m.fiancee.name, ")" 13