# -*- coding: cp1252 -*- # -*- coding: utf-8 -*- ################################################ # Módulo: appl_bij. Aplicaciones biyectivas. # # Autor: dserrano, bcurtu. # # Versión 1.0. Enero de 2009. # ################################################ """ Aplicaciones biyectivas. Resumen: La clase ApplBij le permitirá extraer aleatoriamente aplicaciones biyectivas a partir de una matriz (booleana) de correspondencias. Abstract: ApplBij class allows you to randomly extract bijective applications from a (boolean) matrix of correspondences. """ import os import random import time # Comprobación de si el argumento T es una matriz (lista anidada). def IsMatrix(T): "Comprobación de si el argumento T es una matriz (lista anidada)." if type(T)!=type([]) or len(T)==0: return False for i in range(len(T)): if type(T[i])!=type([]) or len(T[i])!=len(T[0]): return False return True # Comprobación de si una matriz T es cuadrada. def IsSquareMatrix(T): "Comprobación de si una matriz T es cuadrada." if len(T)!=len(T[0]): return False return True # Comprobación de si una matriz T es booleana. def IsBooleanMatrix(T): "Comprobación de si una matriz T es booleana." for i in range(len(T)): for j in range(len(T[0])): if not T[i][j] in (0, False, 1, True): return False return True # Creación de una submatriz resultado de eliminar la fila m y la columna n en la matriz T. def Submatrix(T, m, n): "Creación de una submatriz resultado de eliminar la fila m y la columna n en la matriz T." return [[T[i][j] for j in range(len(T[0])) if j!=n] for i in range(len(T)) if i!=m] # Aplicaciones biyectivas. class ApplBij: """ Clase ApplBij. Aplicaciones biyectivas. --------------------------------------- Dada una matriz booleana y cuadrada A, llamada de correspondencias (o de in- clusiones / exclusiones), crearemos un objeto ApplBij de la forma: import appl_bij ... apli=appl_bij.ApplBij(A, mode) # Objeto ApplBij denominado apli. El parámetro mode puede valer 'SAFE' o 'UNSAFE' y es el modo de inicialización de la clase. .- Cuando un objeto ApplBij se inicializa en modo 'SAFE' se computan las apli- caciones y aplicaciones biyectivas existentes. .- Cuando un objeto ApplBij se inicializa en modo 'UNSAFE' no se computan las aplicaciones biyectivas. Para obtener una aplicación biyectiva al azar, simplemente escribiremos: loopSize=apli.GetLoopSize([alfa]) # (Esta sentencia sólo una vez). result=apli.GetRandomApplBij(loopSize) En el ejemplo anterior result es una lista donde se guardará la aplicación bi- yectiva hallada y alfa es la probabilidad (0.00: draw=random.randint(1, self.NumCorr[j]) else: draw=1 for i in range(len(self.Table)): if self.Table[i][j]: count+=1 if count==draw: Appl.append(i) break else: Appl.append(i+1) return Appl # Constructor. def __init__(self, T, mode): """ Constructor de la clase ApplBij. T: matriz de correspondencias. Es una matriz (en forma de lista anidada) cuadrada y booleana. mode: modo de inicialización de la clase. Puede ser 'SAFE' o 'UNSAFE'. Se lanzará una excepción ValueError si: .- T no es una matriz, no es cuadrada o no es booleana. .- mode no es ni 'SAFE' ni 'UNSAFE'. """ if not (IsMatrix(T) and IsSquareMatrix(T) and IsBooleanMatrix(T)) \ or not mode in ('SAFE', 'UNSAFE'): raise ValueError, 'Base class: ApplBij. Invalid argument(s).' self.Table=T self.mode=mode self.NullAppl=[len(self.Table) for i in range(len(self.Table[0]))] self.NumCorr=[0 for i in range(len(self.Table[0]))] self.NumAppl=self.NumPerm=1 self.NumApplBij=[0] self.density=1.0 self.__Initialize() if self.__AreThereApplBij(): if self.mode=='SAFE': ApplBij.__ComputeNumApplBij(self.Table, self.NumApplBij) else: self.NumApplBij=[1] random.seed() # Obtención del número máximo de aplicaciones al azar que se han de extraer # para obtener, con probabilidad de al menos alfa, una aplicación biyectiva. def GetLoopSize(self, alfa=0.999): """ Obtención del número máximo de aplicaciones al azar que se han de extraer para obtener, con probabilidad de al menos alfa, una aplicación biyectiva. (Esta probabilidad no se puede garantizar si la clase ApplBij se ha ini- cializado en modo 'UNSAFE'). Se lanzará una excepción ValueError si alfa<=0.0 o alfa>=1.0. """ if alfa<=0.0 or alfa>=1.0: raise ValueError, 'Base class: ApplBij. Invalid argument(s).' if self.NumApplBij[0]<=0 or self.density<=0.0: return 0 if self.mode=='UNSAFE': return 10000 if self.density>1.0: p=float(self.NumApplBij[0])/self.NumPerm else: p=float(self.NumApplBij[0])/self.NumAppl i=0 s=0.0 while s1.0: Perm=[i for i in range(len(self.Table))] for i in range(LoopSize): random.shuffle(Perm) if self.__IsCompatible(Perm): self.stat=(len(self.Table), self.mode, self.density, LoopSize, i+1, True) return Perm else: for i in range(LoopSize): Appl=self.__GetRandomAppl() if self.__IsBijective(Appl): self.stat=(len(self.Table), self.mode, self.density, LoopSize, i+1, True) return Appl self.stat=(len(self.Table), self.mode, self.density, LoopSize, LoopSize, False) return None # Obtención de la estadística del sorteo (en forma de tupla). def GetDrawStat(self): """ Obtención de la estadística del sorteo (en forma de tupla). La tupla contiene la siguiente información: .- Tamaño de la matriz de correspondencias. .- Modo de inicialización de la clase ('SAFE' / 'UNSAFE'). .- Densidad de la matriz de correspondencias. .- Tamaño del bucle de búsqueda usado en el sorteo. .- Número real de iteraciones habidas en el sorteo. .- ¿Sorteo exitoso? (True / False). Para obtener la estadística de un sorteo, use este método después de usar el método GetRandomApplBij(). """ return self.stat # Test de la clase ApplBij. def Test(): "Test de la clase ApplBij." # Creación de la matriz de correspondencias para un sorteo del Amigo Invisible # entre parejas. (Uno no puede regalarse ni a sí mismo ni a su pareja). def CreateCorr(size): Corr=[[True for j in range(2*size)] for i in range(2*size)] for i in range(len(Corr)/2): for j in range(2*i, 2*i+2): Corr[2*i+1][j]=Corr[2*i][j]=False return Corr # Creamos la matriz A donde alojamos una correspondencia. A=CreateCorr(6) # Imprimimos la matriz A. print 'Sorteos del Amigo invisible para %d parejas.' % (len(A)/2) print for i in range(len(A)): for j in range(len(A[0])): print '%-5s' % str(A[i][j]), print # Creamos una aplicación biyectiva a partir de la matriz A. # (La inicializamos en modo 'UNSAFE'). appl=ApplBij(A, 'UNSAFE') # Determinamos el tamaño del bucle de búsqueda. loopSize=appl.GetLoopSize() # Buscamos e imprimimos los sorteos exitosos. while(True): result=appl.GetRandomApplBij(loopSize) print 'Resultado del sorteo:' if result: print result print 'Estadística del sorteo:' print appl.GetDrawStat() time.sleep(1) # Ejecución del test de la clase ApplBij. if __name__=='__main__': help(ApplBij) Test()