def solver(C, S): class ParticionesConjuntoPS(PartialSolution): def __init__(self, solucion, sumas_acumuladas): self.solucion = solucion self.sumas_acumuladas = sumas_acumuladas self.n = len(self.solucion) def is_solution(self): return self.n == len(C) def get_solution(self): return self.solucion def successors(self): if self.n < len(C): nuevo_numero = C[self.n] for i in range(S): if self.sumas_acumuladas[i] + nuevo_numero <= suma_maxima: #TODO HACER COPIA DE LA LISTA nueva_lista = self.sumas_acumuladas[:] nueva_lista[i] += nuevo_numero yield ParticionesConjuntoPS(self.solucion + (i, ), nueva_lista) suma_maxima = sum(C) / S sumas_acumuladas = [0] * S initialPS = ParticionesConjuntoPS((), sumas_acumuladas) return BacktrackingSolver.solve(initialPS)
def sudoku_solver(sudoku): class SudokuPS(PartialSolution): def __init__(self, sudoku: Sudoku): self.s = sudoku # Indica si la sol. parcial es ya una solución factible (completa) def is_solution(self) -> bool: return primera_vacia(self.s) is None # Si es sol. factible, la devuelve. Si no lanza excepción def get_solution(self) -> Sudoku: return self.s # Devuelve la lista de sus sol. parciales sucesoras def successors(self) -> Iterable["SudokuPS"]: vacia = primera_vacia(self.s) if vacia is not None: f, c = vacia for posible in posibles_en(self.s, f, c): nuevo_sudoku = deepcopy(self.s) nuevo_sudoku[f][c] = posible yield SudokuPS(nuevo_sudoku) initial_PS = SudokuPS(sudoku) return BacktrackingSolver.solve(initial_PS)
def conjuntos_solver(C, S): class ConjuntoPs(BacktrackingSolver): def __init__(self, ds, subc): self.ds = ds self.n = len(ds) self.subc = subc def is_solution(self): return self.n == len(C) def get_solution(self): return self.ds def successors(self): if self.n < len(C): nr = C[self.n] for i in range(S): if nr + self.subc[i] <= SUMA: subc2 = self.subc[:] subc2[i] += nr yield ConjuntoPs(self.ds + (i, ), subc2) SUMA = sum(C) / S initialPS = ConjuntoPs((), [0] * S) return BacktrackingSolver.solve(initialPS)
def hamiltoniancycle_solver(g: UndirectedGraph) -> Solution: class HamiltonianCycle(PartialSolution): def __init__(self, solution: Tuple, used: Set): self.solution = solution self.n = len(solution) self.used = used def is_solution(self) -> bool: return self.n == len(g.V) and self.solution[0] in g.succs( self.solution[-1]) def get_solution(self) -> Solution: return self.solution def successors(self) -> Iterable["PartialSolution"]: if self.n < len(g.V): for suc in g.succs(self.solution[self.n - 1]): if suc not in self.used: new_used = deepcopy(self.used) new_used.add(suc) yield HamiltonianCycle(self.solution + (suc, ), new_used) vertice = next(iter(g.V)) usados = set() usados.add(vertice) initialPS = HamiltonianCycle((vertice, ), usados) return BacktrackingSolver.solve(initialPS)
def cryptoSolver(palabras: list): class CryptoAPS(PartialSolution): def __init__(self): self.vistas = len(asignaciones.keys()) def is_solution(self) -> bool: return n_letras == self.vistas def get_solution(self) -> Solution: return dict(asignaciones) def successors(self) -> Iterable["PartialSolution"]: if n_letras > self.vistas: l = letras[self.vistas] for i in range(inicio(l, palabras), 10): if i not in numeros: asignaciones[l] = i if factible(asignaciones, matriz_letras): numeros.add(i) yield CryptoAPS() numeros.remove(i) del asignaciones[l] numeros = set() asignaciones = {} matriz_letras, letras = mat_letras(palabras) n_letras = len(letras) initialPS = CryptoAPS() return BacktrackingSolver.solve(initialPS)
def camino_suave_solver(v_ini, g, d, k, total_aristas): class PartialPS(PartialSolution): def __init__(self, ds: list, ar, v_ini): self.ds = ds self.n = len(ds) self.v_ini = v_ini self.ultima_ar = ar def is_solution(self) -> bool: if self.n > 0: if g.succs(self.ds[-1]) > 0: return False return True def get_solution(self) -> Solution: return self.ds def successors(self) -> Iterable["PartialSolution"]: if self.n < len(g.V): for v in g.succs(self.v_ini): if self.n == 0: arista_actual = (v_ini, v) ds2 = self.ds[:] ds2.append(v_ini) ds2.append(v) yield PartialPS(ds2, arista_actual, v) else: peso_ultima_arista = d(self.ultima_ar) arista_actual = (self.ds[-1], v) peso_arista_actual = d(self.ds[-1], v) if abs(peso_arista_actual - peso_ultima_arista) <= k: ds2 = self.ds[:] ds2.append(v) yield PartialPS(ds2, arista_actual, v) aristas_comprobadas = dict() aristas_a_comprobar = dict() for arista in g.E: aristas_comprobadas[arista] = 0 for u, v in g.E: if g.in_degree(u) == 0: aristas_a_comprobar[(u, v)] = 1 elif g.in_degree(u) > 0 and g.out_degree(u) > 0: # si le llegan aristas y tiene sucesor, le añado el valor de sucesor aristas_a_comprobar[(u, v)] = g.in_degree(u) # elif g.in_degree(u) > 0 and g.out_degree(v) == 0: # aristas_a_comprobar[(u, v)] = 1 # print("Comprobadas",aristas_comprobadas) # print("A comprobar",aristas_a_comprobar) initialPS = PartialPS([], (), v_ini) return BacktrackingSolver.solve(initialPS)
def domino_solver(f): class DominoPS(BacktrackingSolver): def __init__(self, ds, fs): self.ds = ds self.n = len(ds) self.fs = fs def is_solution(self): return self.n == len(f) def get_solution(self): return self.ds def successors(self): if self.n < len(f): for ficha in self.fs: if self.n == 0: # copia fs2 = self.fs[:] fs2.remove(ficha) yield DominoPS(self.ds + (ficha, ), fs2) # ficha[::-1] da la vuelta a la tupla yield DominoPS(self.ds + (ficha[::-1], ), fs2) else: ultima_ficha = self.ds[-1] # comprobamos que b[i] == a[i] if ultima_ficha[1] == ficha[ 0]: # no hay que girar la ficha # copia fs2 = self.fs[:] fs2.remove(ficha) yield DominoPS(self.ds + (ficha, ), fs2) # comprobamos que b[i] == b[i] elif ultima_ficha[1] == ficha[ 1]: # si hay que girar la ficha # copia fs2 = self.fs[:] fs2.remove(ficha) yield DominoPS(self.ds + (ficha[::-1], ), fs2) initialPS = DominoPS((), f) return BacktrackingSolver.solve(initialPS)
def sumandos_solver(problema, S): class BuscaSumandosPS(PartialSolution): def __init__(self, solucion, suma): self.solucion = solucion self.n = len(self.solucion) self.suma = suma def is_solution(self): return self.n == len(problema) and self.suma == S def get_solution(self): return self.solucion def successors(self): if self.n < len(problema): for numero in problema[self.n]: if self.suma + numero <= S: yield BuscaSumandosPS(self.solucion + (numero,), self.suma + numero) initial_ps = BuscaSumandosPS((), 0) return BacktrackingSolver.solve(initial_ps)
def grafo_solver(g, vertices_grafo): class CicloHM(PartialSolution): def __init__(self, ds): self.ds = ds self.n = len(ds) self.vertices = vertices_grafo def is_solution(self): return self.n == len(g.V) and self.ds[0] in g.succs(self.ds[-1]) def get_solution(self): return self.ds def successors(self): if self.n < len(g.V): for v in g.succs(self.vertices[self.n]): if v not in self.ds: yield CicloHM(self.ds + (v, )) initial_ps = CicloHM(()) return BacktrackingSolver.solve(initial_ps)
def sumandos_solver(problema: List[List[int]], S: int) -> Iterable: class BuscandoSumandoPS(PartialSolution): def __init__(self, sol: Tuple, s: int): self.sol = sol self.n = len(sol) self.s = s def is_solution(self) -> bool: return self.n == len(problema) and self.s == S def get_solution(self) -> Solution: return self.sol def successors(self) -> Iterable["PartialSolution"]: if self.n < len(problema): for elem in problema[self.n]: suma = self.s + elem if suma <= S: yield BuscandoSumandoPS(self.sol + (elem, ), suma) initialPS = BuscandoSumandoPS((), 0) return BacktrackingSolver.solve(initialPS)
def sumandos_solver(S, lista_numeros): class BuscaSumandosPS(PartialSolution): def __init__(self, solution, quedan): self.solution = solution self.quedan = quedan self.n = len(solution) def is_solution(self) -> bool: return self.quedan == S def get_solution(self) -> Solution: return self.solution def successors(self) -> Iterable["KnapsackPS"]: for item in lista_numeros[self.n]: if item + self.suma <= S: yield BuscaSumandosPS(self.solution + (item, ), self.quedan + item) initialPS = BuscaSumandosPS( (), 0) # IMPLEMENTAR: Añade los parámetros que tú consideres return BacktrackingSolver.solve(initialPS)
def lista_solver(C, S): class ListaPs(PartialSolution): def __init__(self, ds, sumaLocal): self.ds = ds self.n = len(ds) self.sumaLocal = sumaLocal def is_solution(self): return self.n == len(C) and self.sumaLocal == S def get_solution(self): return self.ds def successors(self): if self.n < len(C): for i in C[self.n]: if i + self.sumaLocal <= S: yield ListaPs(self.ds + [ i, ], self.sumaLocal + i) initialPS = ListaPs([], 0) return BacktrackingSolver.solve(initialPS)
def crypto_solver(words): #print(words) class CryptoAPS(PartialSolution): def __init__( self, dict, char_index, word_index, digits_left, sum, current_letter ): self.dict = dict self.n = len(dict) self.char_index = char_index self.word_index = word_index self.digits_left = digits_left self.n_words = len(words) self.n_chars = len(words[-1]) self.sum = sum self.current_letter = current_letter def is_solution(self) -> bool: if self.n != n_letters(words): return False if self.dict[words[-1][0]] == 0: return False hidden = [] for word in words: number = 0 for i in range(1, len(word)+1): number += self.dict[word[-i]] * 10 ** (i - 1) #print(number) hidden.append(number) return sum(hidden[:-1]) == hidden[-1] def get_solution(self) -> Solution: return self.dict def feasible(self, digit: int, run_call: bool) -> bool: #print("sum, digit, index, word index", self.sum, digit, self.char_index, self.word_index) known_col = self.word_index < self.n_words - 1 extra_value = 0 add_digit = 0 if not run_call: add_digit = digit for i in range(self.word_index + 1, self.n_words): word = words[i] is_there = self.char_index <= len(word) known = not is_there or word[-self.char_index] in self.dict.keys() #print(known) if not known: known_col = False break if is_there and i < self.n_words - 1: extra_value += self.dict[word[-self.char_index]] if known_col: #print("Esta ", words[-1][-self.char_index], "en dict:", # words[-1][-self.char_index] in self.dict.keys()) part = (self.sum + (add_digit + extra_value) * 10 ** (self.char_index - 1)) left = (part // (10 ** (self.char_index - 1))) % 10 letter_o = words[-1][-self.char_index] right = self.dict[letter_o] coherent = left == right #print( # "Am ", self.current_letter, ", in (", self.word_index, self.char_index, "letters below value", # extra_value, "total sum is ", self.sum, "plus mine ,",digit,"." # ". Part is ", part, ". That makes ",right, ", which is value of ", letter_o, # "but actually ", left, coherent) #print("char", self.char_index) right_c = (self.sum // (10 ** (self.char_index - 1))) % 10 return not (digit == 0 and self.char_index == len(words[self.word_index])) and ( self.word_index < self.n_words - 1 and ( not known_col or known_col and coherent) or not self.word_index < self.n_words - 1 and digit == (self.sum // (10 ** (self.char_index - 1))) % 10 ) def next_letter(self) -> string: if self.word_index == self.n_words - 1: self.word_index = 0 self.char_index += 1 else: self.word_index += 1 if self.char_index <= len(words[self.word_index]): return words[self.word_index][-self.char_index] else: return None def successors(self) -> Iterable["CryptoAPS"]: last = self.word_index == self.n_words and self.char_index == len(words[-1]) #print("Lo pillo en", self.word_index, self.char_index, self.current_letter) right_path = True default = True #print("FUERA: ", self.current_letter) while self.current_letter is None or self.current_letter in self.dict.keys(): right_path = default or self.current_letter is None or self.feasible(self.dict[self.current_letter], True) if not right_path: break #print(self.current_letter, "llamando a next,", self.char_index) self.current_letter = self.next_letter() #print(self.current_letter, "después de next,", self.char_index) #print("E none?", self.current_letter is None) default = False if self.word_index != self.n_words - 1 and self.current_letter in self.dict.keys(): #print("Esto sucede en letra", self.current_letter) self.sum += self.dict[self.current_letter] * 10 ** (self.char_index - 1) #print(self.current_letter) #print(self.word_index, self.char_index) #print(self.current_letter) if right_path: for digit in self.digits_left: #print("try " + self.current_letter + " = " + str(digit)) if self.feasible(digit, False): sum = self.sum #print("feasible") copy_dict = deepcopy(self.dict) copy_dict[self.current_letter] = digit if self.word_index != self.n_words - 1: sum += digit * 10 ** (self.char_index - 1) digits_left = set(number for number in self.digits_left if number != digit) #print(copy_dict) #print(self.n, n_letters(words)) if self.n != n_letters(words) or last: yield CryptoAPS( copy_dict, self.char_index, self.word_index, digits_left, sum, self.current_letter ) #print("Lo dejo en", self.word_index, self.char_index, self.current_letter) initial_pc = CryptoAPS( {}, 1, 0, set(n for n in range(10)), 0, words[0][-1] ) # dict, char_index, word_index, digits_left, sum, current_letter return BacktrackingSolver.solve(initial_pc)
def camino_suave_solver(v_ini, g, d, k, total_aristas): class PartialPS(PartialSolution): def __init__(self, ds, ar: list, ultima_arista, contador): self.ds = ds self.n = len(ds) self.ar = ar self.ultima_arista = ultima_arista self.contador = contador def is_solution(self) -> bool: for polla in aristas_a_comprobar.items(): arista = polla[0] valor = polla[1] if aristas_comprobadas[arista] != valor: return False return True def get_solution(self) -> Solution: return self.ds def successors(self) -> Iterable["PartialSolution"]: # if self.n < len(g.E): for arista in self.ar: if self.n == 0: for v in g.succs(v_ini): arista_actual = (v_ini, v) ar2 = self.ar.copy() ar2.remove(arista_actual) aristas_comprobadas[arista_actual] = 1 yield PartialPS(self.ds + (v_ini, ) + (v, ), ar2, arista_actual, self.contador + 1) else: # comprobar si la ultima arista tiene camino con la arista actual if self.ultima_arista[1] == arista[0]: peso_arista_actual = d(arista) peso_arista_ultima = d(self.ultima_arista) # comprobamos que realmente sea un camino valido: <= k if abs(peso_arista_actual - peso_arista_ultima) <= k: ar2 = self.ar.copy() # ar2.remove(arista) arista_actual = (self.ultima_arista[1], arista[1]) yield PartialPS(self.ds + (arista[1], ), ar2, arista_actual, self.contador + 1) else: # resetear aristas_comprobadas for arista in g.E: aristas_comprobadas[arista] = 0 break # borrar el la arista en la cual estoy e irme a la arista anterior # ar2 = self.ar.copy() # ar2.remove(arista) # self.ar.remove(arista) # yield PartialPS(self.ds, ar2, self.ultima_arista, self.contador + 1) else: # no hay camino para esta arista, borramos la 1a arista de todas las aristas ar2 = self.ar.copy() ar2.remove(arista) aristas_comprobadas[arista] += 1 # self.ar.remove(arista) # yield PartialPS(self.ds, ar2, self.ultima_arista, self.contador + 1) # self.contador += 1 aristas_comprobadas = dict() aristas_a_comprobar = dict() for arista in g.E: aristas_comprobadas[arista] = 0 for u, v in g.E: if g.in_degree(u) == 0: aristas_a_comprobar[(u, v)] = 1 elif g.in_degree(u) > 0 and g.out_degree(u) > 0: # si le llegan aristas y tiene sucesor, le añado el valor de sucesor aristas_a_comprobar[(u, v)] = g.in_degree(u) initialPS = PartialPS((), list(g.E), (), 0) return BacktrackingSolver.solve(initialPS)
new_sudoku[f][c] = item yield SudokuPS(new_sudoku) # PROGRAMA PRINCIPAL ------------------------------------------------------- if __name__ == "__main__": init = time.time() # m_sudoku = [[0, 0, 0, 3, 1, 6, 0, 5, 9], [0, 0, 6, 0, 0, 0, 8, 0, 7], [0, 0, 0, 0, 0, 0, 2, 0, 0], # [0, 5, 0, 0, 3, 0, 0, 9, 0], [7, 9, 0, 6, 0, 2, 0, 1, 8], [0, 1, 0, 0, 8, 0, 0, 4, 0], # [0, 0, 8, 0, 0, 0, 0, 0, 0], [3, 0, 9, 0, 0, 0, 6, 0, 0], [5, 6, 0, 8, 4, 7, 0, 0, 0]] # El sudoku más difícil del mundo m_sudoku = [[8, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 6, 0, 0, 0, 0, 0], [0, 7, 0, 0, 9, 0, 2, 0, 0], [0, 5, 0, 0, 0, 7, 0, 0, 0], [0, 0, 0, 0, 4, 5, 7, 0, 0], [0, 0, 0, 1, 0, 0, 0, 3, 0], [0, 0, 1, 0, 0, 0, 0, 6, 8], [0, 0, 8, 5, 0, 0, 0, 1, 0], [0, 9, 0, 0, 0, 0, 4, 0, 0]] print("Original:") pretty_print(m_sudoku) print("\nSoluciones:") # Mostrar todas las soluciones # IMPLEMENTAR utilizando SudokuPS y BacktrackingSolver for solution in BacktrackingSolver.solve(SudokuPS(m_sudoku)): pretty_print(solution) print("Solution found at: ", time.time() - init, "seconds.") print( "<TERMINDADO>" ) # Espera a ver este mensaje para saber que el programa ha terminado print("Program ended asudoku_easy.pyt: ", time.time() - init, "seconds.")
for num in posibles_en(self.s, f, c): copia_sudoku = deepcopy(self.s) copia_sudoku[f][c] = num yield SudokuPS(copia_sudoku) # PROGRAMA PRINCIPAL ------------------------------------------------------- if __name__ == "__main__": #m_sudoku = [[0, 0, 0, 3, 1, 6, 0, 5, 9], [0, 0, 6, 0, 0, 0, 8, 0, 7], [0, 0, 0, 0, 0, 0, 2, 0, 0], # [0, 5, 0, 0, 3, 0, 0, 9, 0], [7, 9, 0, 6, 0, 2, 0, 1, 8], [0, 1, 0, 0, 8, 0, 0, 4, 0], # [0, 0, 8, 0, 0, 0, 0, 0, 0], [3, 0, 9, 0, 0, 0, 6, 0, 0], [5, 6, 0, 8, 4, 7, 0, 0, 0]] # El sudoku más difícil del mundo m_sudoku = [[8, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 6, 0, 0, 0, 0, 0], [0, 7, 0, 0, 9, 0, 2, 0, 0], [0, 5, 0, 0, 0, 7, 0, 0, 0], [0, 0, 0, 0, 4, 5, 7, 0, 0], [0, 0, 0, 1, 0, 0, 0, 3, 0], [0, 0, 1, 0, 0, 0, 0, 6, 8], [0, 0, 8, 5, 0, 0, 0, 1, 0], [0, 9, 0, 0, 0, 0, 4, 0, 0]] print("Original:") pretty_print(m_sudoku) print("\nSoluciones:") # Mostrar todas las soluciones # IMPLEMENTAR utilizando SudokuPS y BacktrackingSolver initial_PS = SudokuPS(m_sudoku) for solution in BacktrackingSolver.solve(initial_PS): pretty_print(solution) print( "<TERMINDADO>" ) # Espera a ver este mensaje para saber que el programa ha terminado
def distribucion_solve(almacenA, almacenB, fabricas, costeA, costeB, componentes): class DistribucionPS(PartialSolution): def __init__(self, coste, solucion, almacenA, almacenB, fabricas, costeA, costeB, componentes): self.coste = coste self.solucion = solucion self.almacenA = almacenA self.almacenB = almacenB self.fabricas = fabricas self.costeA = costeA self.costeB = costeB self.componentes = componentes def is_solution(self) -> bool: return len(self.solucion) == self.fabricas def get_solution( self) -> Solution: #Tuple[int, List[Tuple[int, int]]]: return (self.coste, self.solucion) def successors(self) -> Iterable["DistribucionPS"]: if not self.is_solution(): fabrica = len(self.solucion) if self.componentes[fabrica] < self.almacenA: if self.componentes[fabrica] < self.almacenB: n_comp = 0 while n_comp <= componentes[fabrica]: coste_transporte = ( self.costeA[fabrica] * n_comp) + self.costeB[fabrica] * ( self.componentes[fabrica] - n_comp) s = self.solucion[:] s.append( (n_comp, self.componentes[fabrica] - n_comp)) yield DistribucionPS( self.coste + coste_transporte, s, self.almacenA - n_comp, self.almacenB - (self.componentes[fabrica] - n_comp), self.fabricas, self.costeA, self.costeB, self.componentes) n_comp += 1 else: n_comp = 0 # for hasta almacenB while n_comp <= self.almacenB: coste_transporte = (self.costeA[fabrica] * (self.componentes[fabrica]-(self.almacenB-n_comp))) + \ self.costeB[fabrica] * (self.almacenB-n_comp) s = self.solucion[:] s.append(((self.componentes[fabrica] - (self.almacenB - n_comp), (self.almacenB - n_comp)))) yield DistribucionPS( self.coste + coste_transporte, s, self.almacenA - (self.componentes[fabrica] - (self.almacenB - n_comp)), self.almacenB - (self.almacenB - n_comp), self.fabricas, self.costeA, self.costeB, self.componentes) n_comp += 1 else: n_comp = 0 # for hasta almacenA while n_comp <= self.almacenA: coste_transporte = (self.costeA[fabrica] * (self.almacenA-n_comp)) + \ self.costeB[fabrica] * (self.componentes[fabrica]-(self.almacenA-n_comp)) s = self.solucion[:] s.append(((self.almacenA - n_comp), (self.componentes[fabrica] - (self.almacenA - n_comp)))) yield DistribucionPS( self.coste + coste_transporte, s, self.almacenA - (self.almacenA - n_comp), self.almacenB - (self.componentes[fabrica] - (self.almacenB - n_comp)), self.fabricas, self.costeA, self.costeB, self.componentes) n_comp += 1 initialPS = DistribucionPS(0, [], almacenA, almacenB, fabricas, costeA, costeB, componentes) print("hi") return BacktrackingSolver.solve(initialPS)