人工智能是研究使计算机来模拟人的某些思惟过程和智能行为(如学习、推理、思考、规划等)的学科,主要包括计算机实现智能的原理、制造相似于人脑智能的计算机,使计算机能实现更高层次的应用。人工智能将涉及到计算机科学、心理学、哲学和语言学等学科。能够说几乎是天然科学和社会科学的全部学科,其范围已远远超出了计算机科学的范畴,人工智能与思惟科学的关系是实践和理论的关系,人工智能是处于思惟科学的技术应用层次,是它的一个应用分支。从思惟观点看,人工智能不只限于逻辑思惟,要考虑形象思惟、灵感思惟才能促进人工智能的突破性的发展,数学常被认为是多种学科的基础科学,数学也进入语言、思惟领域,人工智能学科也必须借用数学工具,数学不只在标准逻辑、模糊数学等范围发挥做用,数学进入人工智能学科,它们将互相促进而更快地发展。html
遗传算法是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、天然选择以及杂交等。遗传算法一般实现方式为一种计算机模拟。对于一个最优化问题,必定数量的候选解(称为个体)的抽象表示(称为染色体)的种群向更好的解进化。传统上,解用二进制表示(即0和1的串),但也能够用其余表示方法。进化从彻底随机个体的种群开始,以后一代一代发生。在每一代中,整个种群的适应度被评价,从当前种群中随机地选择多个个体(基于它们的适应度),经过天然选择和突变产生新的生命种群,该种群在算法的下一次迭代中成为当前种群。算法
用C++语言编写和调试一个用遗传算法解旅行商TSP问题的程序。目的是学会运用知识表示方法和搜索策略求解一些考验智力的简单问题,熟悉简单智能算法的开发过程并理解其实现原理。windows
用遗传算法解旅行商TSP问题:假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每一个城市只能拜访一次,并且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为全部路径之中的最小值。数组
在遗传算法解旅行商TSP问题当中程序整体围绕了遗传算法的三个主要步骤:选择--复制,交叉,变异。给定了10个种群,即10条染色体,每条染色体都是除首位外不重复的点组成,首尾相同保证路线是闭合的,因此一条染色体包含11个点。数据结构
遗传算法解旅行商TSP问题实验原理:函数
TSP问题就是寻找一条最短的遍历n个城市的最短路径,即搜索天然数子集W={1,2..n}(W的元素表示对n个城市的编号)的一个排列。工具
遗传算法是具备“生成+检测”的迭代过程的搜索算法。它的基本处理流程如图1所示。由此流程图可见,遗传算法是一种群体型操做,该操做以群体中的全部个体为对象。选择( Selection)、交叉(Crossover)和变异(Mutation) 是遗传算法的3个主要操做算子,它们构成了所谓的遗传操做( genetic operation),使遗传算法具备了其它传统方法所没有的特性。遗传算子包含以下6个基本因素:性能
(1)参数编码:因为遗传算法不能直接处理解空间的解数据,所以必须经过编码将它们表示成遗传空间的基因型串结构数据。学习
(2)生成初始群体:因为遗传算法的群体型操做须要,因此必须为遗传操做准备一个由若干初始解组成的初始群体。初始群体的每一个个体都是经过随机方法产生。优化
( 3)适应度评估检测:遗传算法在搜索进化过程当中通常不须要其余外部信息,仅用适应度( fitness) 值来评估个体或解的优劣,并做为之后遗传操做的依据。
(4)选择(selection): 选择或复制操做是为了从当前群体中选出优良的个体,使它们有机会做为父代为下一代繁殖子孙。个体适应度越高,其被选择的机会就越多。此处采用与适用度成比例的几率方法进行选择。具体地说,就是首先计算群体中全部个体适应度的总和,再计算每一个个体的适应度所占的比例,并以此做为相应的选择几率。
(5)交叉操做:交叉操做是遗传算法中最主要的遗传操做。简单的交叉(即一点交叉)可分两步进行:首先对种群中个体进行随机配对:其次,在配对个体中随机设定交叉处,配对个体彼此交换部分信息。
(6)变异:变异操做是按位(bit) 进行的,即把某一位的内容进行变异。变异操做一样也是随机进行的。通常而言,变异几率P都取得较小。变异操做是十分微妙的遗传操做,它须要和交叉操做配合使用,目的是挖掘群体中个体的多样性,克服有可能限于局部解的弊病。
遗传算法解旅行商TSP问题程序功能结构图:
流程图:
数据结构定义:
//定义染色体的结构
struct Chrom
{
int cityArr[cityNum]; //染色体的基因编码
char name; //染色体的名称
float adapt; //染色体的适应度
int dis; //染色体的路径长度
};
struct Chrom genes[popSize]; //定义基因库(结构体数组)
struct Chrom genesNew[popSize]; //从新创建一个新的种群
struct Chrom temp; //定义临时公用结点
names[cityNum] = {'A','B','C','D','E','F','G','H','I','J'}; //城市名称
distance[cityNum][cityNum] //城市距离矩阵
函数方法定义:
initGroup() //初始化基因库
popFitness() //计算种群全部染色体的个体适应度
chooseBest() //返回最优秀的一条染色体
select() // 选择操做
cross() //交叉操做
mutation() //变异操做
遗传算法解旅行商TSP问题C语言代码:
#include "stdio.h" #include "stdlib.h" #include "windows.h" #include "time.h" #define cityNum 10 //城市数量(基因数量)(染色体长度) #define popSize 10 //种群大小(尺寸) #define croRate 0.85 //交叉几率 #define mutRate 0.1 //变异几率 #define MAX 999 //进化代数 //定义染色体的结构 struct Chrom { int cityArr[cityNum]; //染色体的基因编码 char name; //染色体的名称 float adapt; //染色体的适应度 int dis; //染色体的路径长度 }; struct Chrom genes[popSize]; //定义基因库(结构体数组) struct Chrom genesNew[popSize]; //从新创建一个新的种群 struct Chrom temp; //定义临时公用结点 char names[cityNum] = {'A','B','C','D','E','F','G','H','I','J'}; //城市名称 int distance[cityNum][cityNum] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, //城市距离矩阵 { 1, 0, 1, 2, 3, 4, 5, 6, 7, 8 }, { 2, 1, 0, 1, 2, 3, 4, 5, 6, 7 }, { 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }, { 4, 3, 2, 1, 0, 1, 2, 3, 4, 5 }, { 5, 4, 3, 2, 1, 0, 1, 2, 3, 4 }, { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3 }, { 7, 6, 5, 4, 3, 2, 1, 0, 1, 2 }, { 8, 7, 6, 5, 4, 3, 2, 1, 0, 1 }, { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }}; //最优解为18 void initGroup() { //初始化基因库 int i,j,k; int t = 0; int flag = 0; srand(time(NULL));//初始化随机种子,防止随机数每次重复,经常使用系统时间来初始化,当srand()的参数值固定的时候,rand()得到的数也是固定的 for(i = 0; i < popSize; i ++) { //使用临时结点开始赋值 temp.name = names[i]; temp.adapt = 0.0f; temp.dis = 0; //产生10个不相同的数字 for(j = 0; j < cityNum;) { t = rand()%cityNum; //随机产生0-9的数 flag = 1; for(k = 0; k < j; k ++) { if(genes[i].cityArr[k] == t) { flag = 0; break; } } if(flag) { temp.cityArr[j] = t; genes[i] = temp;//存入结构体数组,产生一个个体 j++; } } } } //计算种群全部染色体的个体适应度 void popFitness() { int i,n1,n2; for(i = 0; i < popSize; i ++) { genes[i].dis = 0; for(int j = 1;j < cityNum; j ++) { n1 = genes[i].cityArr[j-1]; n2 = genes[i].cityArr[j]; genes[i].dis += distance[n1][n2]; } genes[i].dis += distance[genes[i].cityArr[0]][genes[i].cityArr[cityNum-1]]; genes[i].adapt = (float)1/genes[i].dis; //每条染色体的路径总和(个体适应度) } } //返回最优秀的一条染色体 int chooseBest() { int choose = 0; float best = 0.0f; best = genes[0].adapt; for(int i = 0; i < popSize; i ++) { if(genes[i].adapt < best) { best = genes[i].adapt; choose = i; } } return choose; } // 选择操做 void select() { float biggestSum = 0.0f; float adapt_pro[popSize]; float pick = 0.0f; int i; for(i = 0; i < popSize; i ++) { biggestSum += genes[i].adapt; // 总几率 } for(i = 0; i < popSize; i ++) { adapt_pro[i] = genes[i].adapt / biggestSum; // 几率数组 } // 轮盘赌 for(i = 0;i < popSize; i ++) { pick = (float)rand()/RAND_MAX; // 0到1之间的随机数 for(int j = 0; j < popSize; j ++) { pick = pick - adapt_pro[j]; if(pick <= 0) { genesNew[i] = genes[j]; break; } } } for(i = 0;i < popSize; i++) { genes[i] = genesNew[i]; } } void cross() // 交叉操做 { float pick; int choice1,choice2; int pos1,pos2; int temp; int conflict1[popSize]; // 冲突位置 int conflict2[popSize]; int num1; int num2; int index1,index2; int move = 0; // 当前移动的位置 while(move < popSize-1) { pick = (float)rand()/RAND_MAX; // 用于决定是否进行交叉操做 if(pick > croRate) //两条染色体是否相爱 { move += 2; continue; // 本次不进行交叉 } // 采用部分映射杂交 choice1 = move; // 用于选取杂交的两个父代 choice2 = move+1; // 注意避免下标越界 pos1 = rand()%popSize; pos2 = rand()%popSize; while(pos1 > popSize -2 || pos1 < 1)//若是位置在开头或结尾(由于所有交换无心义) { pos1 = rand()%popSize; } while(pos2 > popSize -2 || pos2 < 1) { pos2 = rand()%popSize; } if(pos1 > pos2) { temp = pos1; pos1 = pos2; pos2 = temp; // 交换pos1和pos2的位置 } for(int j = pos1;j <= pos2; j++)// 逐个交换顺序 { temp = genes[choice1].cityArr[j]; genes[choice1].cityArr[j] = genes[choice2].cityArr[j]; genes[choice2].cityArr[j] = temp; } num1 = 0; num2 = 0; if(pos1 > 0 && pos2 < popSize - 1)//分三段 { for(int j =0;j < pos1;j++) { for(int k = pos1; k <= pos2; k++) { if(genes[choice1].cityArr[j] == genes[choice1].cityArr[k]) { conflict1[num1] = j; num1 ++; } if(genes[choice2].cityArr[j] == genes[choice2].cityArr[k]) { conflict2[num2] = j; num2 ++; } } } for(j = pos2 + 1;j < popSize;j++) { for(int k = pos1; k <= pos2; k ++) { if(genes[choice1].cityArr[j] == genes[choice1].cityArr[k]) { conflict1[num1] = j; num1 ++; } if(genes[choice2].cityArr[j] == genes[choice2].cityArr[k]) { conflict2[num2] = j; num2 ++; } } } } if((num1 == num2) && num1 > 0) { for(int j = 0;j < num1; j ++) { index1 = conflict1[j]; index2 = conflict2[j]; temp = genes[choice1].cityArr[index1]; // 交换冲突的位置 genes[choice1].cityArr[index1] = genes[choice2].cityArr[index2]; genes[choice2].cityArr[index2] = temp; } } move += 2; } } //变异操做 void mutation() { double pick; int pos1,pos2,temp; for(int i = 0;i < popSize; i ++) { pick = (float)rand()/RAND_MAX; // 用于判断是否进行变异操做 if(pick > mutRate) { continue; } pos1 = rand()%popSize; pos2 = rand()%popSize; while(pos1 > popSize - 1) { pos1 = rand()%popSize; } while(pos2 > popSize - 1) { pos2 = rand()%popSize; } int a = genes[i].dis; temp = genes[i].cityArr[pos1]; genes[i].cityArr[pos1] = genes[i].cityArr[pos2]; genes[i].cityArr[pos2] = temp; popFitness();//更新数据 //此步骤的做用在于检查是否变异后获得的个体比变异前更优秀了,如若往坏的方向变化了,那还不如不变异了 //(强制返回,虽然有点违背事物的客观发展规律,但为了加强程序的收敛性,该操做仍是有必要的)(偷笑) if(genes[i].dis > a) { temp = genes[i].cityArr[pos1]; genes[i].cityArr[pos1] = genes[i].cityArr[pos2]; genes[i].cityArr[pos2] = temp; } } } int main() { char c = 0; printf("\n\t\t******************************** 遗传算法求解TSP(旅行商)问题 *********************************\n"); initGroup(); //初始化 popFitness(); //更新数据 //输出配置信息 printf("\n\t\t基因长度:%d",cityNum); printf("\t种群大小:%d",popSize); printf("\t交叉几率:%.2f",croRate); printf("\t变异几率:%.2f",mutRate); printf("\t进化代数:%d",MAX); printf("\t预设最优解:18"); printf("\n\n\t\t**********************************************************************************************\n"); //输出距离矩阵 printf("\n\n\t\t--------- 城市距离矩阵 ---------\n"); printf("\t\t"); int i,j; for(i = 0; i < cityNum; i ++) { for(j = 0;j < cityNum; j ++) { printf(" %d",distance[i][j]); } printf("\n\t\t"); } printf("--------------------------------\n"); //输出路径信息 printf("\n\t\t-------- 初始种群基因库 --------\n"); printf("\t\t "); for(i = 0; i < cityNum; i ++) { printf(" %c",genes[i].name); } printf("\n\t\t"); for(i = 0;i < cityNum; i ++) { printf("%c",genes[i].name); for(j = 0; j < cityNum; j ++) { printf(" %d",genes[i].cityArr[j]); } printf("\n\t\t"); } printf("--------------------------------\n"); do { printf("\n\t\t寻求最优解中:"); //经过不断进化,直到达到定义的进化代数 for(i = 0; i < MAX; i ++) { select(); cross(); popFitness();//更新数据 mutation(); popFitness();//更新数据 int temp = (int)MAX/20; } printf("完成"); printf("\n\n\t\t最优路径:"); for(i = 0; i < cityNum ; i++) { printf("%d-->",genes[chooseBest()].cityArr[i]); } printf("%d",genes[chooseBest()].cityArr[0]); printf("\n\n\t\t路径长度:%d",genes[chooseBest()].dis); printf("\n\n\t\t是否再试一次?(Y/y) 是/(N/n) 否:"); fflush(stdin); c = getchar(); fflush(stdin); if(c=='N'||c=='n') { break; } }while(1); printf("\n\t\t"); return 0; }
在旅行商问题当中由遗传算法对以上状况的求解(10座城市),能够看出,用传算法来求解TSP问题是可行的。用遗传算法求解时,增长选代次数,能够获得更加优良的结果,可是会须要更长的时间,即一个优良的结果每每是以时间力代价的,这种状况要依据具体问题具体分析,平衡二者在问题求解时的比重。参数设置得好坏每每对遗传算法的性能和结果有着重要的影响,每每在解决某一问题的时候,须要设定的参数不少,如何得到最佳的参数组合是一个很重要的问题。另外,对选择算法进行优化,会提升遗传算法的性能,这些都须要在实践中不断科累和普遍涉猎优良算法。最后,遗传算法获得的未必是最优解,咱们能够根据须要进行屡次求解,从而比较得出符合要求的结果。