TSP问题, 旅行商问题,即TSP问题(Travelling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
贪心算法:又称贪婪算法(greedy algorithm),该算法是指:在对问题求解时,总是做出当前情况下的最好选择,否则将来可能会后悔,故名“贪心”。这是一种算法策略,每次选择得到的都是局部最优解。选择的策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。即考虑局部最优解以达到整体最优解的情况,但是贪心算法不一定是最优解的情况,接近最优解.
贪心算法是保证每一步的选择都是对于当前而言是最优的,这种选择方法并不能保证一定是最优解,但是通常也是非常近似最优解。对于TSP问题利用贪心选择策略,从某一城市出发,下一个城市的选择是依据是,距离该城市距离最近的城市,作为下一个城市。并且该城市未被访问,直到经过了所有城市,然后回到起始城市。
程序的逻辑结构采取为无向图。存储结构采取邻接矩阵存储城市信息.
利用标记数组flag进行保存结点是否访问信息。利用变量记录城市的数量,利用一个字符数组存放各个城市名称。并对城市名称进行编码。
1.首先用户通过界面菜单,录入城市信息,完成创建后可以通过显示邻接矩阵来查看录入的信息,这里定义一个MAX变量表示两个城市之间无通路。
- 然后用户录入完成城市信息后可以选择选择路径的选项来找到对应的路径选择。在录入城市信息时,动态创建对应的邻接矩阵,名称,标记数组。通过提示信息,提示用户进行输入,根据输入的城市名称,利用转化函数,获取对应的城市编号,如果不存在则赋值一个新的编号,进行路径信息的录入。提示用户结束的操作。
- 名称与编号转化的函数,主要通过记录已经录入城市的数量,每次新录入的时候,查询已经录入的信息,如果存在返回对应的编号(即坐标),否则新建编号,即对应已记录城市的数量即新编号,数量增加。
- 初始化信息数组的信息,利用malloc进行动态创建数组。
获取路径函数:从用户获取起始城市,从起始城市开始,对到达下一城市的路径进行贪心(即选择最短的那个城市),直到所有城市被访问,最后回到起始城市。
TSPLIP可以下载数据集进行测试。读取TSPLIP数据集的方法,根据不同类型,采用不同方法进行读取.
结构定义
char typeflag[] = "EDGE_WEIGHT_TYPE" ;
char typeflag2[] = "EDGE_WEIGHT_TYPE:" ;
char type[50] ; // 存储城市类型
char formatType[50] ; //存储边类型
char numflag[] = "DIMENSION" ;
char numflag2[] = "DIMENSION:" ;
char dataflag[] = "NODE_COORD_SECTION";
char dataflag2[] = "DISPLAY_DATA_SECTION" ;
char typeflag3[] = "EDGE_WEIGHT_FORMAT:" ;
char typeflag4[] = "EDGE_WEIGHT_FORMAT" ;
char typeflag5[] = "EDGE_WEIGHT_SECTION" ;
读取各种类型数据.
void readATT(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int i = 0 ;
int flag = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,dataflag)==0)
{
flag =1 ;
}
}
else
{
fscanf(file,"%lf",&point[i][0]) ;
fscanf(file,"%lf",&point[i][1]) ;
i++ ;
}
} fclose(file) ;
}
void readEXPLICIT(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int i = 0 ;
int flag = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,dataflag2)==0)
{
flag =1 ;
}
}
else
{
fscanf(file,"%lf",&point[i][0]) ;
fscanf(file,"%lf",&point[i][1]) ;
i++ ;
}
}
fclose(file) ;
}
void readEXPLICITLow(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int j ;
int flag = 0 ;
int x = 0 , y = 0 ;
int temps = 0 ;
int num = 1 , i = 0 ; ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,typeflag5)==0)
{
flag =1 ;
}
}
else
{
if(strcmp(temp,dataflag2)==0||strcmp(temp,"EOF")==0)
return ;
if(i<num)
{
map[num-1][i] = atoi(temp) ;
i++ ;
}
else
{
num++ ; i = 0 ;
map[num-1][i] = atoi(temp) ;
}
for(j=0;j<N-1;j++)
{
fscanf(file,"%s",temp) ;
if(i<num)
{
map[num-1][i] = atoi(temp) ;
i++ ;
}
else
{
num++ ; i = 0 ;
map[num-1][i] = atoi(temp) ;
}
}
}
}
fclose(file) ;
}
void readEXPLICITUp(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int j ;
int flag = 0 ;
int x = 0 , y = 0 ;
int temps = 0 ;
int num = 1 , i = N-1 , k = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,typeflag5)==0)
{
flag =1 ;
}
}
else
{
if(strcmp(temp,dataflag2)==0||strcmp(temp,"EOF")==0)
return ;
if(i>k)
{
map[num-1][i] = atoi(temp) ;
i-- ;
}
else
{
num++ ; i = N-1 ;
map[num-1][i] = atoi(temp) ;
}
for(j=0;j<N-k-1;j++)
{
fscanf(file,"%s",temp) ;
if(i>k)
{
map[num-1][i] = atoi(temp) ;
i-- ;
}
else
{
num++ ; i = N-1 ;
map[num-1][i] = atoi(temp) ;
}
}
k++;
}
}
fclose(file) ;
}
读取最优解,则通过在网站下载最优解的数据集进行读取即可,根据不同数据文件,读取不同数据
void getBestDate(char aim[])
{
FILE *file = fopen("bestdate.txt","r") ;
char temp[100] ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,aim)==0)
{
fscanf(file,"%s",temp) ;
fscanf(file,"%s",temp) ;
bestdates = atoi(temp) ;
return ;
}
}
}
TSP问题解决的算法实现:
void getPath()
{
char start[20] ;
int now_position , weight , i , j , next_position , count = 0 , start_position , num;
double min = MAX ;
double sum ;
sum = 0 ;
now_position = 0 ;
start_position = now_position ;
flag[now_position] = 1 ;
count = 1 ;
num = N ;
while(count!=num)
{
min = MAX ;
for(i=0;i<num;i++)
{
//不是本身,并且没有被访问过,进行访问
if(i!=now_position&&flag[i]!=1)
{
//找到距离当前结点最近的距离
if(map[now_position][i]<min)
{
min = map[now_position][i] ;
next_position = i ;
}
}
}
if(min==MAX)
{
printf("找不到连通所有结点的一条路径\n") ;
return ;
}
else
{
printf("%d 从%d\t到\t%d 下一城市为%d 路径长度为%lf\n",count,now_position+1,next_position+1,next_position+1,map[now_position][next_position]);
sum += map[now_position][next_position] ;
flag[next_position] = 1 ;
now_position = next_position ;
count++ ;
}
if(count==num)
{
printf("%d 从%d\t到\t%d 下一城市为%d 路径长度为%lf\n",count,now_position+1,next_position+1,now_position+1,map[next_position][start_position]);
sum += map[next_position][start_position] ;
}
}
printf("总路径长度:%lf 最优解为%lf 精确度%.2lf\n",sum,bestdates,sum/bestdates) ;
}
整个程序的代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#define MAX 9999999999 // 表示最大的距离
char typeflag[] = "EDGE_WEIGHT_TYPE" ;
char typeflag2[] = "EDGE_WEIGHT_TYPE:" ;
char type[50] ; // 存储城市类型
char formatType[50] ; //存储边类型
char numflag[] = "DIMENSION" ;
char numflag2[] = "DIMENSION:" ;
char dataflag[] = "NODE_COORD_SECTION";
char dataflag2[] = "DISPLAY_DATA_SECTION" ;
char typeflag3[] = "EDGE_WEIGHT_FORMAT:" ;
char typeflag4[] = "EDGE_WEIGHT_FORMAT" ;
char typeflag5[] = "EDGE_WEIGHT_SECTION" ;
double bestdates ;
int N ;
int * flag ;
double **map;
double **point ;
void getPath() ;
void getType(char a[])
{
a = strcat(a,".tsp") ;
FILE *file = fopen(a,"r") ;
char temp[100] ;
int i;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,numflag)==0)
{
for(i=0;i<2;i++)
fscanf(file,"%s",temp) ;
N = atoi(temp) ;
}
if(strcmp(temp,numflag2)==0)
{
fscanf(file,"%s",temp) ;
N = atoi(temp) ;
}
if(strcmp(temp,typeflag)==0)
{
for(i=0;i<2;i++)
fscanf(file,"%s",temp) ;
strcpy(type,temp) ;
}
if(strcmp(temp,typeflag2)==0)
{
fscanf(file,"%s",temp) ;
strcpy(type,temp) ;
}
if(strcmp(temp,typeflag3)==0)
{
fscanf(file,"%s",formatType) ;
return ;
}
if(strcmp(temp,typeflag4)==0)
{
fscanf(file,"%s",temp) ;
fscanf(file,"%s",formatType) ;
return ;
}
}
fclose(file) ;
};
void readATT(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int i = 0 ;
int flag = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,dataflag)==0)
{
flag =1 ;
}
}
else
{
fscanf(file,"%lf",&point[i][0]) ;
fscanf(file,"%lf",&point[i][1]) ;
i++ ;
}
} fclose(file) ;
}
void readEXPLICIT(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int i = 0 ;
int flag = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,dataflag2)==0)
{
flag =1 ;
}
}
else
{
fscanf(file,"%lf",&point[i][0]) ;
fscanf(file,"%lf",&point[i][1]) ;
i++ ;
}
}
fclose(file) ;
}
void readEXPLICITLow(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int j ;
int flag = 0 ;
int x = 0 , y = 0 ;
int temps = 0 ;
int num = 1 , i = 0 ; ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,typeflag5)==0)
{
flag =1 ;
}
}
else
{
if(strcmp(temp,dataflag2)==0||strcmp(temp,"EOF")==0)
return ;
if(i<num)
{
map[num-1][i] = atoi(temp) ;
i++ ;
}
else
{
num++ ; i = 0 ;
map[num-1][i] = atoi(temp) ;
}
for(j=0;j<N-1;j++)
{
fscanf(file,"%s",temp) ;
if(i<num)
{
map[num-1][i] = atoi(temp) ;
i++ ;
}
else
{
num++ ; i = 0 ;
map[num-1][i] = atoi(temp) ;
}
}
}
}
fclose(file) ;
}
void readEXPLICITUp(char a[])
{
FILE *file = fopen(a,"r") ;
char temp[100] ;
int j ;
int flag = 0 ;
int x = 0 , y = 0 ;
int temps = 0 ;
int num = 1 , i = N-1 , k = 0 ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,"EOF")==0)
break ;
if(flag==0)
{
if(strcmp(temp,typeflag5)==0)
{
flag =1 ;
}
}
else
{
if(strcmp(temp,dataflag2)==0||strcmp(temp,"EOF")==0)
return ;
if(i>k)
{
map[num-1][i] = atoi(temp) ;
i-- ;
}
else
{
num++ ; i = N-1 ;
map[num-1][i] = atoi(temp) ;
}
for(j=0;j<N-k-1;j++)
{
fscanf(file,"%s",temp) ;
if(i>k)
{
map[num-1][i] = atoi(temp) ;
i-- ;
}
else
{
num++ ; i = N-1 ;
map[num-1][i] = atoi(temp) ;
}
}
k++;
}
}
fclose(file) ;
}
void getBestDate(char aim[])
{
FILE *file = fopen("bestdate.txt","r") ;
char temp[100] ;
while(!feof(file))
{
fscanf(file,"%s",temp) ;
if(strcmp(temp,aim)==0)
{
fscanf(file,"%s",temp) ;
fscanf(file,"%s",temp) ;
bestdates = atoi(temp) ;
return ;
}
}
}
int main()
{
int i , j ;
char files[100] = "brazil58" ;
char aim[100] ;
bestdates = 0 ;
// printf("输入文件名(例如att48.tsp输入att48即可):") ;
// scanf("%s",files) ;
strcpy(aim,files) ;
getType(files) ;
getBestDate(aim) ;
printf("%d %s\n",N,type) ;
map = (double**)malloc(sizeof(double *)*N) ;
point = (double**)malloc(sizeof(double *)*N) ;
flag = (int*)malloc(sizeof(int *)*(N)) ;
printf("%s\n",formatType) ;
for(i=0;i<N;i++)
{
map[i] = (double*) malloc(sizeof(double)*N) ;
point[i] = (double*)malloc(sizeof(double)*2) ;
flag[i] = 0 ;
}
if(strcmp(type,"ATT")==0||strcmp(type,"GEO")==0||strcmp(type,"EUC_2D")==0||strcmp(type,"CEIL_2D")==0)
{
readATT(files) ;
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
if(j==i)
map[i][j] = 0 ;
else
map[i][j] = sqrt((point[j][0]-point[i][0])*(point[j][0]-point[i][0])+(point[j][1]-point[i][1])*(point[j][1]-point[i][1])) ;
}
}
}
else if (strcmp(type,"EXPLICIT")==0&&strcmp(formatType,"LOWER_DIAG_ROW")==0)
{
readEXPLICITLow(files) ;
for(i=0;i<N;i++)
{
for(j=0;j<=i;j++)
{
if(i==j)
map[i][j] = 0 ;
else
map[j][i] = map[i][j] ;
}
}
}
else if (strcmp(type,"EXPLICIT")==0&&(strcmp(formatType,"UPPER_DIAG_ROW")==0||(strcmp(formatType,"UPPER_ROW")==0)))
{
readEXPLICITUp(files) ;
for(i=0;i<N;i++)
{
for(j=i;j<N;j++)
{
if(i==j)
map[i][j] = 0 ;
else
map[j][i] = map[i][j] ;
}
}
}
else if(strcmp(formatType,"FULL_MATRIX")==0)
{
readEXPLICIT(files) ;
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
if(j==i)
map[i][j] = 0 ;
else
map[i][j] = sqrt((point[j][0]-point[i][0])*(point[j][0]-point[i][0])+(point[j][1]-point[i][1])*(point[j][1]-point[i][1])) ;
}
}
}
/*
读取的路径信息
*/
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
printf("%lf \n",map[i][j]) ;
}
int begintime , endtime ;
begintime = clock() ;
getPath() ;
endtime = clock() ;
printf("贪婪算法运行了%dms\n",endtime-begintime) ;
return 0 ;
}
void getPath()
{
char start[20] ;
int now_position , weight , i , j , next_position , count = 0 , start_position , num;
double min = MAX ;
double sum ;
sum = 0 ;
now_position = 0 ;
start_position = now_position ;
flag[now_position] = 1 ;
count = 1 ;
num = N ;
while(count!=num)
{
min = MAX ;
for(i=0;i<num;i++)
{
//不是本身,并且没有被访问过,进行访问
if(i!=now_position&&flag[i]!=1)
{
//找到距离当前结点最近的距离
if(map[now_position][i]<min)
{
min = map[now_position][i] ;
next_position = i ;
}
}
}
if(min==MAX)
{
printf("找不到连通所有结点的一条路径\n") ;
return ;
}
else
{
printf("%d 从%d\t到\t%d 下一城市为%d 路径长度为%lf\n",count,now_position+1,next_position+1,next_position+1,map[now_position][next_position]);
sum += map[now_position][next_position] ;
flag[next_position] = 1 ;
now_position = next_position ;
count++ ;
}
if(count==num)
{
printf("%d 从%d\t到\t%d 下一城市为%d 路径长度为%lf\n",count,now_position+1,next_position+1,now_position+1,map[next_position][start_position]);
sum += map[next_position][start_position] ;
}
}
printf("总路径长度:%lf 最优解为%lf 精确度%.2lf\n",sum,bestdates,sum/bestdates) ;
}