Algoritmo genético - rotas

Publicado por César tinum da silva (última atualização em 08/12/2009)

[ Hits: 23.501 ]

Homepage: www.google.com

Download AG.py




Bom galera, aí vai um algoritmo genético desenvolvido para resolver a heurística do caixeiro viajante utilizando programação paralela.

Qualquer dúvida meu email está no arquivo py. Favor manter a nota de autoria. Tentei comentar o código ao máximo para melhor compreensão posterior.

Para executar, entre no diretório onde se encontra o arquivo py pelo terminal e digite "python AG.py".

  



Esconder código-fonte

############################################################
#ALGORITMO GENETICO DESENVOLVIDO PARA DISCIPLINA INTELIGENCIA ARTIFICIAL   #
#OBJETIVO: SOLUCAO EFICIENTE PARA O PROBLEMA DE ROTEAMENTO DE VEICULOS     #
#AUTOR: CESAR TINUM ,SABRINE HENRIQUE           DATA:06-11-2009                           #
#CENTRO UNIVERSITARIO DE BELO HORIZONTE DCE -       7 PERIODO MANHA                  #
#CONTATO :             magodoschatsbh@gmail.com , sabrine@2xt.com.br                           #
############################################################


#SELECAO     #MUTACAO   #CROSSOVER
import random                                 #GERACAO NUMEROS ALEATORIOS
from threading import Thread               #BIBLIOTECA DE GERACAO DE SUBPROCESSOS (THREADS)
from math import ceil                        #BIBLIOTECA DE ARREDONDAMENTO NUMERICO
from time import time, strftime
class AG:
   def __init__(self, mutacao,crossover,tamPopulacao,fitnes, cidades, maxdistancia, geracoes, numthreads, naoalcanco):
      start = time()
      self.MAX_DIST = maxdistancia
      self.mutacao = mutacao                      #propriedade de mutacao
      self.crossover = crossover
      self.NAOALCANCO = naoalcanco            #possibilidade de nao haver caminho entre cidades
      self.tamPopulacao = tamPopulacao          #numero populacao inicial
      self.fitnes = fitnes                           #funcao de avaliacao
      self.populacao = []                           #vetor contendo a populacao
      self.fitPopulacao = []                        #vetor com resultado avaliacao de cada elem da populacao
      self.distancia = {}
      self.NUM_THREADS = numthreads
      self.geracoes = geracoes
      self. cidades = cidades
      self.disCidades(cidades)
      self.populacaoInicial = []
      self.__initGeracao()
      self.selecao()
      self.populacaoInicial = self.populacao
      for i in range(self.geracoes):
         self.__nextGeracao()
         self.__crossover()
      self.selecao()
      finish = time()
      self.time = finish - start
      self.__header()
   #FIM CLASSE INICIALIZACAO
   def __initGeracao(self):
      try:
         threads = []
         for i in range(self.NUM_THREADS):
            threads.append(self.__startThreads('self.geraPopulacao', {}))                                 #DISPARANDO THREADS PARA GERAR POPULACAO E AVALIACAO DOS ELEMENTOS
         self.__endThreads(threads)                                                                                 #ESPERA THREADS ACABAREM
      except Exception, e:
         print e
   def __nextGeracao(self):
      try:
         threads = []
         tamMutacao = round(ceil(float(self.tamPopulacao) / 2)* self.mutacao)                                          #NUMERO DE INDIVIDUOS QUE SOFRERAO MUTACAO
         faixa = int(ceil(tamMutacao / self.NUM_THREADS))
         r = int((tamMutacao>self.NUM_THREADS and self.NUM_THREADS or tamMutacao))                     #VERIFICA SE NUM THREADS E MAIOR QUE NUM ELEMENTOS QUE SOFRERAO MUTACAO
         for i in range(r):
            threads.append(self.__startThreads('self.geraMutacao', {'ini':i+1, 'tam':faixa}))            #DISPARANDO THREADS PARA EFETUAR MUTACAO NOS PIORES ELEMENTOS DA POPULACAO EM FAIXAS
         self.__endThreads(threads)                                                                                    #ESPERA THREADS ACABAREM
      except Exception, e:
         print e
   def __endThreads(self, threads):
      for t in threads:
         t.join(10)
      return
      
   def geraMutacao(self,ini,  tam):
      try:
         sorteados = []                                                                                    #GUARDA ELEMENTOS SORTEADOS PARA QUE O ALGORITMO NAO ENTRE EM LOOP SORTEANDO SEMPRE O MESMO NODO
         tamC = self.tamPopulacao
         ini = tamC - (2*ini)
         flag = True
         limite = ini + tam
         numCidades = len(self.cidades)
         while ini <= limite:
            tenhoCidades = True
            if flag:
               cont = 0
               posCorte = random.randint(3, numCidades -1)                                                                     #PONTO DE CORTE PARA MUTACAO ALEATORIO
               n = numCidades-posCorte                                                                                             #NUMERO DE ELEMENTOS QUE SERAO TROCADOS
               list = self.populacao[ini][2:posCorte]
               del self.populacao[ini][posCorte:]                                                                                       #DELETO O RESTO DO VETOR A PARTIR DO PONTO DE CORTE PARA INSERIR NOVOS ELEMENTOS
               sorteados = []
               i = 0
               j = 0
            while tenhoCidades:
               flag = False
               nodo = self.cidades[random.randint(0, numCidades-1)]
               if  cont > numCidades - 1:
                  flag = True
                  break
               if nodo in sorteados or nodo in list:
                  cont+=1
                  continue
               try:
                  if self.populacao[ini][posCorte - 1]:
                     d = (self.adjacencia(nodo, self.populacao[ini][posCorte-1])  and True or None)
                  else:
                     d = True
               except Exception, e:
                  d = None
               if d:
                  self.populacao[ini].append(nodo)
                  list.append(nodo)
                  res = self.geraFitnes(self.populacao[ini], 2)
                  self.populacao[ini][0] = res[0]
                  self.populacao[ini][1] = res[1]
                  i+=1
               tenhoCidades = not self.__sublist(list, self.cidades)
            if not flag:
               ini+=1
         return True
      except Exception, e:
         return False
   def __startThreads(self, funcAlvo, k):
      try:
         t = Thread(target = eval(funcAlvo), kwargs = k)
         t.start()
         return t
      except Exception, e:
         print  e
   def disCidades(self, cidades):                                                               #GERA DISTANCIA ENTRE CIDADES , MATRIZ DE ADJACENCIA
      for i in cidades:
         self.distancia[i] = {}
         for j in cidades:
            if  not self.distancia[i].has_key(j):                                                    #SE J NAO ESTA NO DICIONARIO DA CIDADE I VOU INCLUI-LA
               if self.distancia.has_key(j) and j!=i:                                                #SE CIDADE J ESTA NO DICIONARIO (JA TENHO VALOR) E J!=I (NAO E A MESMA CIDADE)
                  self.distancia[i][j] = self.distancia[j][i]
                  continue
               if i == j:
                  self.distancia[i][j] = 0
                  continue
               alcanco = random.randint(1, self.NAOALCANCO)               #CHANCE DE CIDADES NAO SE ALCANCAREM = 1 EM NAO ALCANCO (PARAMETRO)
               if alcanco == 1:
                  self.distancia[i][j] = 100000                                    #ATRIBUI VALOR DISTANCIA MUITO ALTO = CIDADE NAO ALCANCAVEL
                  continue
               self.distancia[i][j] = random.randint(1, self.MAX_DIST)   #SORTEIA VALOR DA DISTANCIA ALEATORIAMENTE ENTRE 1 - MAX_DIST SE FOR A MESMA CIDADE VALOR DISTANCIA 0
      return 
   def selecao(self):                                                                              #DIVIDIR A POPULACAO EM GRUPOS PARA APLICAR FUNCAO DE AVALIACAO POR THREADS.
      self.populacao.sort()
   def geraPopulacao(self):
      tam = int(ceil(float(self.tamPopulacao)/4))
      try:
         for i in range(tam):
            d = []
            sorteados = []
            d.append(self.cidades[random.randint(0, len(self.cidades) - 1)])                                                                                                               #1 CIDADE ALEATORIA
            sorteados.append(d[0])
            cont = 1
            while not self.__sublist(d, self.cidades):                                                                                                #RODA LOOP ENQUANTO NAO TEM TODAS CIDADES NA ROTA - SE IMPASSE ATE 100 ITERACOES - COMECA DE NOVO
               b = self.cidades[random.randint(0, len(self.cidades) - 1)]
               cont+=1
               if cont >len(self.cidades):
                  cont = 0
                  d = []
                  sorteados = []
                  d.append(self.cidades[random.randint(0, len(self.cidades) - 1)])                                                                                                               #1 CIDADE ALEATORIA
                  sorteados.append(d[0])
               elif b in sorteados:
                  continue
               sorteados.append(b)
               c = (self.adjacencia(d[len(d)-1],b) and d.append(b) or False)                                                #VERIFICA SE CIDADES SAO ADJACENTES (EXISTE ROTA ENTRE ELAS)
            if len(self.populacao) == self.tamPopulacao:                                                                     #COMO AS THREADS PODEM SE DIVIDIR EM MAIS ELEMENTOS QUE O TAMANHO ORIGINAL PRAMETRIZADO
               return False                                                                                                         #DEVIDO AO ARREDONDAMENTO DE VEZES PARA CADA THREAD CRIAR ELEMENTOS
            res = self.geraFitnes(d, 0)                                                                                          #VERIFICO AQUI SE TAMANHO TOTAL DE ELEMENTOS JA FOI CRIADO ANTES SE INSERIR CADA ELEMENTO
            d.insert(0, res[0])
            d.insert(1, res[1])
            self.populacao.append(d)
         return True
      except Exception, e:
         print e

   def adjacencia(self, a, b):
      return ((self.distancia[a][b]<9999 and a!=b or False) and True or False)            #SE A DISTANCIA MENOR QUE 9999 AVALIA SE A!=B PARA EVITAR IR E VOLTAR NA MESMA CIDADE
   def geraFitnes(self, mat, pos):                                                                   #AVALIA CADA ROTA POSSIVEL DA POPULACAO
      try:
         d = []
         e = mat[pos:]
         mat = mat[pos:]
         f = self.cidades
         tenhoTodasCidades = ((self.__sublist(mat, f) and True or False) and 1 or 100)                     #VERIFICA SE ROTA SENDO AVALIADA POSSUI TODAS AS CIDADES 
         d.extend(self.distancia[mat[i]][mat[i+1]] for i in range(len(mat)))            
         return [tenhoTodasCidades, sum(d)]                                                   #SOMA VALOR DE DISTANCIA ENTRE CIDADES DA ROTA 
      except Exception, e:
         return [tenhoTodasCidades, sum(d)]
   def load(self, url):
      try:
         file = open(url, 'r')
         fileLines= file.readlines()
         for linha in fileLines:
            linha = linha.split(" ")
            for indice, distancia in enumerate(linha):
               pass
         #LE CADA LINHA E ATRIBUI VALOR DE DISTANCIAS   
      except Exception, e:
         print e
   def __sublist(self, filho,  mae):
      try:
         contem=[]
         contem.extend((item in filho and 1 or 0) for item in mae)                              #VERIFICA SE ROTA POSSUI TODAS CIDADES
         if 0 in contem:
            return False
         else:
            return True
      except Exception, e:
         print 'Erro na busca de sublista, ', e
   def __crossover(self):                                                                           #IMENDA ROTAS MAIS BEM AVALIADAS EM ROTAS MAL AVALIADAS A PARTIR DE PONTO EM COMUM
      try:
         tamCrossOver = round(ceil(self.tamPopulacao / 2)* self.crossover)                                          #NUMERO DE INDIVIDUOS QUE SOFRERAO MUTACAO
         faixa = int(ceil(tamCrossOver / self.NUM_THREADS))
         r = int((tamCrossOver>self.NUM_THREADS and self.NUM_THREADS or tamCrossOver))                     #VERIFICA SE NUM THREADS E MAIOR QUE NUM ELEMENTOS QUE SOFRERAO MUTACAO
         threads = []
         for i in range(r):
            threads.append(self.__startThreads('self.geraCrossOver', {'ini':i+1, 'tam':faixa}))            #DISPARANDO THREADS PARA EFETUAR MUTACAO NOS PIORES ELEMENTOS DA POPULACAO EM FAIXAS
         self.__endThreads(threads)
      except Exception, e:
         print e
   def geraCrossOver(self, ini, tam, nodo=None):
      try:
         tamC = self.tamPopulacao
         ini = tamC - (tam*ini)
         flag = True
         limite = ini + tam
         numCidades = len(self.cidades)
         melhorRota = self.populacao[0][2:]                     #AS CIDADES DA MELHOR ROTA, 2 EM DIANTE POS 0 E 1 SAO AVALIACOES
         while ini < limite:
            if limite>=self.tamPopulacao:
               break
            while nodo == None:
               posCorte = random.randint(3, len(self.cidades)-1)                                                      #PONTO DE CORTE ONDE SERA FEITO A JUNCAO
               nodo = (lambda nodo: nodo in melhorRota and nodo or None)(self.populacao[ini][posCorte])
            del self.populacao[ini][:2]                                                                                    #RETIRA VALORES AVALIACAO
            v = {len(self.populacao[ini][:self.populacao[ini].index(nodo)]):'esq', len(self.populacao[ini][self.populacao[ini].index(nodo):])-1:'dir'}         #NUMERO DE ELEMENTOS A DIREITA E ESQUERD DA POSICAO DE CORTE
            g = {len(melhorRota[:melhorRota.index(nodo)]):'esq', len(melhorRota[melhorRota.index(nodo):])-1:'dir'}         #NUMERO DE ELEMENTOS A DIREITA E ESQUERD DA POSICAO DE CORTE DA MELHOR ROTA
            ladoV = max(v.items())                                                                                    #CRUZAR O LADO QUE TIVER MAIOR NUMERO DE ELEMENTOS [0] = LADO [1] = NUM ELEM
            ladoG = max(g.items())                                                                           #CRUZAR O LADO QUE TIVER MAIOR NUMERO DE ELEMENTOS [0] = LADO [1] = NUM ELEM
            cont=0
            c=0
            ######################################################   
            #VALIDAR NUM ELEMENTOS EQUIPARANDO AO VETOR BASE ( MELHOR ROTA)
            ######################################################   
            if ladoG[0]<ladoV[0]:
               ladoV = min(v.items())                                                                           #MANTER O LADO DO MELHOR VETOR COMO MAIOR PARA NAO FALTAR INDICE
            indiceNodo = self.populacao[ini].index(nodo) 
            indiceNodoG = melhorRota.index(nodo)
            if ladoV[1] == 'esq':
               try:
                  if indiceNodo>0:
                     del self.populacao[ini][:indiceNodo-1]                                                   #DELETA ELEMENTOS A ESQUERDA DO INICIO ATE O INDICE DO VETOR DE JUNCAO
                  indiceNodo = (indiceNodo > 0 and (indiceNodo - 1))
                  for indice in range(indiceNodo):
                     indiceNodoG = (ladoG[1] == 'dir' and (indiceNodoG +1) or (indiceNodoG -1))
                     self.populacao[ini].insert(indice, melhorRota[indiceNodoG])                        #ADICIONANDO ELEMENTOS A DIREITA NO VETOR DE MELHOR ROTA
               except Exception, e:
                  pass
            else:
               try:
                  indiceNodo = self.populacao[ini].index(nodo) 
                  if indiceNodo<len(self.populacao[ini])-1:
                     del self.populacao[ini][indiceNodo+1:]                                                   #DELETA ELEMENTOS A ESQUERDA DO INICIO ATE O INDICE DO VETOR DE JUNCAO
                  indices = []
                  ind = indiceNodo
                  id = len(self.cidades) - indiceNodo
                  while len(indices)  < id:
                     ind+=1
                     indices.append(ind)
                  for indice in indices:
                     indiceNodoG = (ladoG[1] == 'dir' and (indiceNodoG +1) or (indiceNodoG -1))
                     self.populacao[ini].insert(indice, melhorRota[indiceNodoG])                        #ADICIONANDO ELEMENTOS A DIREITA NO VETOR DE MELHOR ROTA
               except Exception, e:
                  pass
            ############################################
            #INSERIR ELEMENTOS PARA QUE ROTA CONTENHA TODAS CIDADES
            ############################################
            list = []
            list= self.populacao[ini]
            c = 0
            try:
               while not self.__sublist(self.populacao[ini], self.cidades) and c<50:                                                            #TENTO 20 VEZES INSERIR ELEMENTO NA LISTA PARA COMPLETA-LA
                  cont=0
                  c+=1
                  elem = self.cidades[random.randint(0, len(self.cidades)-1)]
                  while elem in list and cont < 50:
                     elem = self.cidades[random.randint(0, len(self.cidades)-1)]
                     cont+=1
                  if cont < 50:
                     if self.adjacencia(elem, self.populacao[ini][len(self.populacao[ini])-1]):                                                      #VERIFICA ADJACENCIA ENTRE O ELEMENTO E O ULTIMO DO VETOR
                        self.populacao[ini].append(elem)
                        break
            except Exception, e:
               print 'Erro ao completar rota, ', e
            res = self.geraFitnes(self.populacao[ini], 0)                                                                                          #VERIFICO AQUI SE TAMANHO TOTAL DE ELEMENTOS JA FOI CRIADO ANTES SE INSERIR CADA ELEMENTO
            self.populacao[ini].insert(0, res[0])
            self.populacao[ini].insert(1, res[1])
            ini+=1
      except Exception, e:
         pass
   def __header(self):
      try:
         print "**************************************************************************************"
         print "*   ALGORITMO GENETICO  - HEURISTICA DO CAIXEIRO VIAJANTE              UNI-BH        *"
         print "*   AUTOR: CESAR T. SILVA, SABRINE HEQUER - cesarts25@gmail.com / sabrinesa@gmail.com*"
         print "*   DISCIPLINA: INTELIGENCIA ARTIFICIAL       PROF: ANA PAULA LADEIRA  CCM7          *"
         print "**************************************************************************************"
         print ""
         print "                   PARAMETROS DE ENTRADA PARA O AG             "
         print "PROBABILIDADE DE MUTACAO  = %f"%self.mutacao
         print "PROBABILIDADE DE CRUZAMENTO = %f"%self.crossover
         print "PROBABILIDADE DE NAO ALCANCABILIDADE ENTRE CIDADES = 1/%d"%self.NAOALCANCO
         print "TAMANHO DA POPULACAO = %d"%self.tamPopulacao
         print "CIDADES - ",  self.cidades
         print "DISTANCIA MAXIMA ENTRE AS CIDADES = %d"%self.MAX_DIST
         print "NUMERO DE GERACOES = %d"%self.geracoes
         print "NUMERO DE THREADS UTILIZADAS = %d"%self.NUM_THREADS
         print ""
         print "                   MATRIZ DE ADJACENCIA             "
         c = " "
         for i in self.cidades:
            c += "    "+i
         print c
         for i in self.cidades:
            c = []
            for j in self.cidades:
               c.append(self.distancia[i][j])
            print i, c
         print ""
         print ""
         print "                   RESULTADOS OBTIDOS             "
         print "MELHOR ROTA - ", self.populacao[0][2:]
         print "CUSTO EM KM - ", self.populacao[0][1]
         print "TEMPO GASTO PELO ALGORITMO - ", self.time, " segundos."
         print ""
         pop = raw_input("IMPRIMIR POPULACAO (S/N):  ")
         if pop.lower() =='s':
            print "                   POPULACAO  INICIAL           "
            for i , j in enumerate(self.populacaoInicial):
               print i , '-  ', j
         if pop.lower() =='s':
            print "                   POPULACAO  FINAL           "
            for i , j in enumerate(self.populacao):
               print i , '-  ', j
         print ""
         print "                BELO HORIZONTE %s"%(strftime("%d/%m/%Y"))
      except Exception, e:
         print e

###################
#INSTANCIA CLASSE
####################
if __name__ == "__main__":
   try:
      print "           --  COLETA DE DADOS -- "
      ger = input("NUMERO DE GERACOES: ")
      pop = input("TAMANHO DA POPULACAO: ")
      citys = input("NUMERO DE CIDADES: ")
      c = []
      c.extend(str(city) for city in range(citys))
      print "                    CALCULANDO ROTA..."
      ag =  AG(0.3,0.7,pop,'2x+30x2', c, 1200, ger,  4, 50)
   except Exception, e:
      print e

Scripts recomendados

Combinações de letras, números e muito mais

Calculadora em Python

multimídia tools 0.1 BETA

Manipulação de XML com Python

Criando um jogo de Jokenpo


  

Comentários
[1] Comentário enviado por FIR3FL4M3 em 08/12/2009 - 13:10h

Meu amigo, esse probleminha é uma dor de cabeça e há algum tempo atrás me deparei com um parecido e lembro que rodei um bocado pra achar um exemplo para me guiar.

Valeu pela disponibilização do codigo e parabens.

[2] Comentário enviado por mago_dos_chats em 08/12/2009 - 16:18h

vlw, somente um ajudando o outro que cresceremos.... abraço

[3] Comentário enviado por albfneto em 08/12/2009 - 23:11h

Bonito código! Um amigo meu é especialista em algoritmos genéticos, ele é matemático.

[4] Comentário enviado por mago_dos_chats em 09/12/2009 - 14:38h

vlw albfneto, o codigo ta disponivel ai pra quem quiser usar, so manter a autoria ne.. =)

[5] Comentário enviado por removido em 04/06/2010 - 19:46h

Ai cara programão,parabéns?!?

[6] Comentário enviado por eliaspy em 11/02/2013 - 17:53h

realmente incrível,parabéns !!


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts