Um algoritmo genético para o TSP (Travel Salesman Problem)

Publicado por N M S 18/08/2006

[ Hits: 18.826 ]

Homepage: www.lncc.br/

Download ceget-1.6.cpp




O TSP, problema do caixero viajante, é um problema de alto custo computacional para acharmos a sua solução ótima. Contudo, existem métodos com os quais podemos encontrar boas soluções, aqui apresento um método usando algoritmo genético. O TSP consiste em, dado n cidades que se deva percorrer, passando uma única vez em cada, e, existindo uma estrada entre cada cidade, encontrar o melhor caminho a ser pecorrido.

  



Esconder código-fonte

/***********************************************************
  Caget - Problema do caxeiro viajante resolvido via
          algoritmo genetico.
      por:
          N.M.S.. E-mail nfermat@gnu.cc

          Versao  1.6 -   ultima alteracao 12/08/2004
************************************************************
 Principal Alteração:
        1.5
   Mudança de main para o modo de ordenação
   é feita uma ordenação em ordem crescente de aptidão
   se a diferença entre os extremos for menor que o erro
   permitido será finalizado o programa. Caso contrário
   pegaremos os ultimos termos e aplicaremos mutações
   e crossovers repetindo o processo.
   1.6
   alteracao do quicksort
*****************************************02/10/2003*********/
/*

+--------------+-------+---------------------------------------
|   FUNCAO     |ESTADO | ESPECIFICACAO
+--------------+-------+--------------------------------------
|  mutacao     | OK OK | FAZ A MUTACAO EM UM VETOR
|  crossover   | OK OK | CRUZAMENTO ENTRE DOIS VETORES SOLUCAO
|  sorteio     | OK OK | FUNCAO GENERICA DE SORTEIO ENTRE VETORES NAO ALTERADOS
|  aptidao     | OK OK | DIZ A APTIDAO (DISTACIA TOTAL) DO VETOR SOLUCAO
|  inicio      | OK OK | GERA UMA POPULACAO INICIAL
|  elitismo    | OK OK | GARDA OS MELHORES INDIVIDUOS DA POPULACAO
|  main        |       | FUNCAO PRINCIPAL RESPONSAVEL PELO INICIO E TERMINO
|  melhor      | OK OK | RETORNA A MELHOR SOULUCAO DO ESPACO DE SOLUCOES
|  reset       | OK OK | DESMARCA O ESTADO DE MODIFICADO
|  fim         | OK OK | GRAVA EM ARQUIVO TEMPO DE EXECUCAO E O RESULTADO
|  quicksort1  | OK    | ORDENACAO DOS VETORES
|  separa      | OK    | SEPARRACAO DO QUICKSORT
|  puxa2       | OK OK | FAZ A COLETA DE DADOS NO ARQUIVO
|  entrada     | OK OK | FAZ A COLETA DE DADOS JUNTAMENTE COM PUXA2
+--------------+-------+-------------------------------------
*/
#include <stdlib.h>
#include <stdio.h>
#include <time.h>


#define numcid  400  //numero de cidades envolvidas
#define TESTE   500  //tempo de espera sem evolucao
#define numini  400 //tamanho da populacao de resultados obs deve ser par
#define maximo  999999   //numero maximo de geracoes
#define erroper 100   //erro permitido para o termino
#define MAXCID  500   //numero maximo de cidades

/*
    definicoes do layout arquivo de entrada

    o arquivo de entradas sera definido da seguinte maneira.
    primeira linha -> numero "K" de cidades <= MAXCID
    segunda linha  -> em branco
    proxima linha  -> numero inteiro negativo
    proxima linha  -> numero "X" da cidade
    proximas K linhas -> distancia d(0,n) , n=(0..k-1) obs nesse caso X = 0
    proxima linha  -> em branco
    proxima linha  -> numero inteiro negativo
    proximas K-1 linhas -> distancia d(X,n) , n=(1..k-1)
    proxima linha  -> numero inteiro negativo
    aplica-se um processo indutivo até k-k+1
*/

//   Definição do vetor inicial
//    Primeiro teste com 5 cidades dado no maximo 5! percursos
//   cidade A ponto inicial
//   Definir
//    para as distancias entre as cidades definimos o vetor

int distancia[MAXCID][MAXCID];

//   o vetor solucao e definido da seguinte maneira
//   numcid é o numero de cidades envolvidas no problema
//************************************************************************
//************************************************************************
struct solucao{
   int   cidade[MAXCID]; // vetor solucao propriamente dito
   int   aptidao; // distancia total da solucao
   int   estado; // verifica se ja foi modificado na geração atual
};
void teste(void)
{
   printf("depura\n");
   getchar();
   exit(1);
}
void teste1(void)
{
   printf("depurando");
   getchar();
}

void inicio     (struct solucao *, int, int);
void aptidao    (struct solucao *, int, int);
void crossover  (struct solucao *, int, int, int);
void mutacao    (struct solucao *, int, int );
int  sorteio    (struct solucao *, int );
void elitismo   (struct solucao *, int);
int  melhor     (struct solucao *, int);
void reset      (struct solucao *, int);
int  entrada    (char *, int *);
int  puxa2      (FILE *, int *);
void fim        (struct solucao *, int, int, int, unsigned long int);
void quicksort1 (int *, int *, int, int);
int  separa     (int *, int *, int, int);
int Particiona  (int *, int *, int, int);
void QuickSort  (int *, int *, int, int);
void swap_A     (int *, int *);
void swap_B     (int *, int *);

/*************************************************************
 **********************  MAIN ********************************
 *************************************************************/
int main(void){
   int sortudo,a, sortudo2=0, i,alea, num_cid;
   time_t t_inicial;
   unsigned long int geracao;
   int ordenado0[numini];// endereco dos oordenados
   int ordenado1[numini];// ordenados por aptidao
   struct solucao espaco[numini];// espaco de soulucoes propriamente dito
   t_inicial = time(NULL);
   if (!entrada("entrada.nms", &num_cid))
      return(0);
   for(i=0; i<numini; i++)
   {
      ordenado0[i]=0;
      ordenado1[i]=0;
   }

   srand((unsigned) time(NULL) / 2);
   geracao = 0;
   printf("inicio \n");
   inicio(espaco, num_cid, numini);
   do
   {
           for(i = 0; i < numini; i++)
           {
                   ordenado0[i] = i;
                   ordenado1[i] = espaco[i].aptidao;
           }
      QuickSort(ordenado0, ordenado1, 0, numini-1);
      for(i=0 ; i<numini; i++)
//         printf("i = %d   %d \n",ordenado0[i], ordenado1[i]);
      if((-ordenado1[0] + ordenado1[numini/2]) < erroper)
      {
         break;
      }// if teste final
      i=0;
      a= numini/2;
      for(i = a; i < numini-1; i++)
      {
         if((rand() % 2) == 1)
         {
            crossover(espaco, ordenado0[i] , ordenado0[i+1], num_cid);
            i++;
//            printf("cros\n ");
         }
         else
         {

            mutacao(espaco, ordenado0[i], num_cid);
//            printf("muta %d\n",i);

         }
      }
      geracao=geracao+1;

      i = melhor(espaco, numini);
      printf("geracao %ld - melhor %ld\n",geracao, espaco[melhor(espaco,numini)].aptidao);

      
   } while(geracao < maximo);


   printf("geracao %d \n",geracao);
   fim(espaco, time(NULL)-t_inicial, ordenado0[0], num_cid, geracao);
   return(0);
} //main
//************************************************************************
//  a funcao aptidao eh defivina do seguinte modo
void aptidao(struct solucao *soluc, int quem, int num_cid){
   int resul=0;
   int i;
   for(i=0;i<num_cid-1;i++)
      resul = resul +distancia[soluc[quem].cidade[i]][soluc[quem].cidade[i+1]];
   soluc[quem].aptidao = resul+ distancia[soluc[quem].cidade[0]][soluc[quem].cidade[num_cid-1]];
}
//////*******************************************************************
// Para formar a populacao inicial usaremos a seguinte funcao
void inicio(struct solucao *espaco, int num_cid, int num_inicial){
   int sortudo; // numero selecionado aleatoriamente entre 0 e numcid-1
   int quantos=0; // numero de cidades sorteadas
   int i,j=0,k=0;
   int teste=1;
   srand((unsigned) time(NULL)/2);
   for(i=0;i<num_inicial;i++) // variacao da solucao no espaco de solucoes
   {
      j = 0;
      quantos=0;
      while (j < num_cid)  // variac.da posic da cid no vetor solu
      {
         sortudo = rand()%num_cid;
         for (k = 0; k < quantos; k++)//ver se cid.ja fora sort.
            if (espaco[i].cidade[k] == sortudo)
            {
               teste = 0;
               }
         if (teste){
            espaco[i].cidade[j] = sortudo;
            espaco[i].estado = 0;
            quantos++;
            j++;
         }
         teste=1;
      }
                aptidao(espaco, i, num_cid);
   }
} // fim de inicio

//***********************************************************************

// definiremos a rotina de crossover do seguinte modo
// nessa rotina pegaremos dois vetores pai e mae definidos como inteiros
// e faremos um cruzamento de modo a gerar vetores diferentes

void crossover(struct solucao espaco[], int pai, int mae, int num_cid) {
   int quantos, quem[MAXCID]; // numero de posicoes de pai, vetor de teste
   int i,j,k;
   int filho1[MAXCID], filho2[MAXCID];
   int sortudo, repetido1, repetido2;  //posicao sorteada, teste de repeti
   srand((unsigned) time(NULL)/2);
   for(i=0;i<num_cid;i++) {
       quem[i] = 0;
       filho1[i] = -1;
       filho2[i] = -1;
   }
   quantos =1+ rand()%(num_cid-1);
   i = 0;
   while(i < quantos) {
      sortudo = rand()%(num_cid+1);
      if (!quem[sortudo]) {
            quem[sortudo]=1;
            i++;
      } // if
   }// while
   for(i=0;i<num_cid;i++) {
      if(quem[i]) {
         filho1[i]=espaco[pai].cidade[i];
         filho2[i]=espaco[mae].cidade[i];
      }
   }
   for(i=0;i<num_cid;i++) {
      repetido1=0;
      for(j=0; j<num_cid; j++)
         if(filho1[j] == espaco[mae].cidade[i])
            repetido1 = 1;
      if(repetido1!=1)
      {
         for(j=0; j<num_cid; j++)
            if(filho1[j] == -1){
               repetido1 = 0;
               filho1[j] = espaco[mae].cidade[i];
               j=num_cid+1;
            }  // if filho 1
      }
    }// for i
   for(i=0;i<num_cid;i++) {
     repetido2 = 0;
      for(j=0; j<num_cid; j++)
         if(filho2[j] == espaco[pai].cidade[i])
            repetido2 = 1;
      if(!repetido2)
         for(j=0; j<num_cid; j++)
            if(filho2[j] == -1){
               repetido2 = 0;
               filho2[j] = espaco[pai].cidade[i];
               j=num_cid+1;
            } // if filho 2
   }// for i
   for (i=0; i<num_cid; i++){
      espaco[pai].cidade[i] = filho1[i];
      espaco[mae].cidade[i] = filho2[i];
   }
   espaco[pai].estado = 1;
   espaco[mae].estado = 1;
   aptidao(espaco, pai, num_cid);
   aptidao(espaco, mae, num_cid);
} // fim de crossover

//***********************************************************************
// definiremos a funcao mutacao do seguinte modo
// receberemos o vetor pai e seu tamanho
// faremos um sorteio aleatorio do valor dos genes a serem mudados do cromossomo
// apos sortearemos quais os cromossomos que seram alterados

void mutacao(struct solucao *espaco, int pai, int num_cid)
{
   int sortudo, quantos, achei=0, troca=0;
   int i=0,j,k=0;
   srand((unsigned) time(NULL)/2);
   quantos = rand()%(num_cid+1);
   int quem[MAXCID];
   while (i < quantos){
      achei = 0;
      sortudo = rand()%num_cid;
      for(j=0; j<i; j++)
      {
         if(quem[j] == sortudo)
         {
            achei = 1;
         }
      }
      if(!achei){
         quem[i] = sortudo;
         i++;
      }// if
   } // while
   k=1;
   for(k=1; k < quantos ; k++)
   {
      troca = espaco[pai].cidade[quem[k-1]];
      espaco[pai].cidade[quem[k-1]] = espaco[pai].cidade[quem[k]];
      espaco[pai].cidade[quem[k]] = troca;
      k++;
   }
   espaco[pai].estado = 1;
   return;
} // fim de mutacao
//**********************************************************************
// Funcao Sorteio . Escolhe aleatoriamente uma solucao verficando seu estado
int sorteio(struct solucao espaco[], int tamanho)
{
   int sortudo;
   srand((unsigned) time(NULL)/2);
   while( 1)
   {
      sortudo = rand()%tamanho;
      if(!espaco[sortudo].estado)
         break;
   }// while
   return(sortudo);
}//sorteio

//******************************************************
void elitismo(struct solucao *espaco, int num_inicial)
{
   espaco[melhor(espaco, num_inicial)].estado = 1;
}

//*****************
int melhor(struct solucao *espaco, int num_inicial)
{
   int o=0,i;
   for(i = 0; i < num_inicial; i++)
      if(espaco[i].aptidao < espaco[o].aptidao)
         o = i;
   return(o);
}// melhor

//********************

void reset(struct solucao *espaco, int num_inicial)
{
   int i;
   for(i = 0; i < num_inicial; i++)
      espaco[i].estado = 0;
}//reset

//********************

int puxa2(FILE *arquivo, int *retorno)
{
   int contr;
   contr = fscanf(arquivo, " %d " , retorno);
   return(contr);
}// puxa2
//********************
int entrada(char *arquivo, int *num_cid)
{
   int i,j;
   int cidade=0;
   int teste=0;
   int num=0;
   int zero=0;
   int distan;
   FILE *fp;
   fp = fopen(arquivo,"r");
   if (fp == NULL)
   {
      printf("Erro ao abrir o arquivo de entrada de dados\n");
      return(0);
   }
   rewind(fp);
   printf("teste %d",num);
//   if(puxa(fp, " %d ", &num) == EOF){
   if(puxa2(fp, &num) == EOF){
      printf( "Arquivo em branco\n");
      return(0);
   }
   printf("eu %d vivo \n",num);
   if(num > MAXCID) {
      printf("Numero de cidades maior que o Maximo Permitido\n");
      return(0);
   }
   i=0;
   j=0;
   for(i = 0; i < num; i++)
   {
      if(puxa2(fp, &teste) == EOF )
         if(i < num-1){
            printf("Erro!!! Fim inesperado de arquivo #2.1#\n");
            return(0);
         }// if i
      if (puxa2(fp, &cidade) == EOF)
         if(i < num-1){
            printf("Erro!!! Fim inesperado de arquivo #2.2#\n");
            return(0);
         }// if i
      printf("teste = %d -- cidade =  %d -- i = %d\n", teste, i, cidade);

      if( zero <= teste ){
         printf("Erro na entrada de dados #1.1#\n");
         return(0);
      }//if teste
      if(cidade != i){
         printf("Erro na entrada de dados #1.2#\n");
         return(0);
      }//if teste

      for(j = i; j < num; j++){
         if(puxa2(fp, &distan) != EOF){
            distancia[i][j] = distan;
            distancia[j][i] = distan;
         }//if
         else{
            printf("Erro!!! Fim inesperado de Arquivo #3#\n");
            return(0);
         }//else
      }//for j
   }//for i

   *num_cid = num;
   return(1);
}// entrada
//****************************************************

void fim(struct solucao *espaco, int tempo, int melhor,
            int num_cid, unsigned long int  gera)
{
   FILE *fp;
   int i;
   fp = fopen("saidas.nms","a+");
   if (fp == NULL)
   {
      printf("ERRO na abertura do arquivo de resultados");
      return;
   }
   fprintf(fp,"Resultado\n");
   fprintf(fp," Tempo de execucao %ld .\n",tempo);
   fprintf(fp," Caminho: ");
   for(i=0; i < num_cid; i++)
      fprintf(fp," %d ", espaco[melhor].cidade[i]);
   fprintf(fp, "\nDistancia percorrida: %d\n", espaco[melhor].aptidao);
   fprintf(fp, "Geracao: %d\n\n\n", gera);
}//fim
//********************************************
int Particiona(int B[], int A[], int p, int r)
{
   int x,teste;
   int i,j;
   x = A[p];
   i = p-1;
   j = r+1;
   while(1)
          {
      do 
      {
         j--;
         teste=A[j];
      }while(teste > x && j>0);
//      while(A[j] <= x  && j>0)
//         --j;
      do
      {
         i++;
         teste = A[i];   
      }while((teste < x) && i<r);   
                   //    >   
      if (i<j)
      {
         swap_A(&A[i], &A[j]);
         swap_B(&B[i], &B[j]);
      }
      else
              return(j);
   }

}
void swap_A(int *x, int *y)
{
   int t;
   t = *y;
   *y = *x;
   *x = t;
//   printf("ta");
}
void swap_B(int *x, int *y)
{
   int t;
   t = *y;
   *y = *x;
   *x = t;
//   printf("tb");
   
}

void QuickSort(int B[], int A[] ,int p,int r)
{
   int q;
//   teste1();
   if (p < r){
      q = Particiona(B, A, p, r);
      QuickSort(B, A, p, q);
//      printf("acabei");
      //teste1();
      QuickSort(B, A, q+1, r);
   }
}
 
//*****************************************************************************
//
//      ***************      **************      ************
//    ***************      **************      ************
//   ***              ***      ***      ***
//   ***               ***        ***      ***
//   *********            ***      ***      *********
//   *********            ***      ***      *********
//   ***               ***      ***      ***
//   ***               ***      ***      ***
//   ***************      **************      ***
//   ***************      **************      ***
// nfermat@gnu.cc
//*****************************************************************************

Scripts recomendados

Função para exibir todos os divisores de um numero

Calc Compact

Angelinux Scripts - Palpite para Megasena em C

Desenhando uma curva de Bézier

Contagem de elementos de um array


  

Comentários
[1] Comentário enviado por daniloukb em 07/08/2006 - 20:51h

Olá.

Achei seu script muito interessante, mas parece que está faltando alguma coisa, pois não encontrei explicações sobre como usá-lo. Gostaria muito de uma ajuda.

Grato.

Danilo Fernandes

[2] Comentário enviado por nfermat em 24/08/2006 - 04:23h

Caro Danilo, vc deve criar um arquivo com o nome entradas.nms e este arquivo deve ter o layout descrito no código fonte do programa. Tal arquivo contem a distancia entres as cidades.
NMS

[3] Comentário enviado por pcamaral em 24/11/2009 - 20:17h

Boa Noite!!
Gostaria de uma ajuda na criação do arquivo "entrada.nms". Você poderia me enviar um exemplo deste arquivo? Eu compilei o seu código, porém aparece a mensagem "Erro ao abrir o arquivo de entrada de dados". Preciso de um program URGENTE para resolver o caso do CAIXEIRO VIAJANTE. Certa de sua compreensão.
Atenciosamente,
Paula.

[4] Comentário enviado por nfermat em 26/11/2009 - 01:25h

Paula,

Faz tempo que não mexo nesse programa, contudo
o layout para criação desse arquivo esta no codigo
fonte do programa.

Uma dica, se existe cidades que não tem estradas diretas entre elas,
coloque um número muito grande para a distancia entre elas.

**********
Seuindo o layout, um exemplo para 3 cidade seria:

3

-1
0
0
10
15

-1
1
0
25




Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts