资料整理,仅作记录。
【OpenCV C++】基于遗传算法解决旅行商问题的迭代过程可视化原型系统
功能
- 实现了遗传算法解决旅行商问题
- 实现了可视化旅行地点标记
- 实现了迭代过程可视化
代码
Github
main.cpp
#include"GA_TSP.h"
#include<stdlib.h>
#define BUFSIZE 256
using namespace std;
void main()
{
double t =(double) cv::getTickCount();
system("cls");
generation = 0;
GetPoint();
//fileRead();
//fileWrite();
CITYCOUNT = CHROMLENGTH = pointcount;//城市节点不限制,可以随意改,pointcount为输入的城市节点的个数
CalculatorDistance();
//第一代
GenerateInitialPopulation();
CalculateFitness();
FindBestIndividual();
OutputTextReport();
//然而,不断进化
while (generation<MaxGeneration)
{
generation++;
GenerateNextPopulation();
CalculateFitness();
FindBestIndividual();
ShowResult();
}
//最后一代输出
OutputTextReport();
//结果展示,绘制出了路径
ShowResult();
//fileWrite();
t = cv::getTickCount() - t;
cout << "程序运行时间:" << t / ((double)cv::getTickFrequency()*1000.) <<"ms"<< endl;
//cvWaitKey(0);
}
GA_TSP.cpp
#include "GA_TSP.h"
GA_TSP::GA_TSP()
{
}
GA_TSP::~GA_TSP()
{
}
int PopSize =500; //种群规模
int MaxGeneration = 10000;//进化代数
double Pc = 0.2;//交叉概率
double Pm = 0.005;//变异概率
int CITYCOUNT = 3; //城市个数
int CHROMLENGTH = 3;
int CitySet[500];//城市集合
int WorkSet[100];//染色体解码时的临时工作数组,存放还未访问的城市序列
int TempCitySet[100];//存放染色体解码完成后所经过的城市序列
FILE *f= fopen("city.txt", "rb+");
//int disaster_control = 0;
//double disaster_Pc = 0.3;
//double disaster_Pm = 0.01;
//double m = Pm;//缓存不变
//double c = Pc;
cv::Scalar line = cv::Scalar(255, 0,0);
typedef struct individual{
int chrom[100];//染色体编码
double value; //value值表示路径长度
double fitness; //适应度
}individual;
int generation;
individual bestindividual;//某一代群体中最优个体
individual currentbest;//当前最优个体
individual population[POPSIZE];//
cv::Mat tsp_image = cv::Mat::zeros(cv::Size(800, 600),CV_8UC3);
int CityDistance[100][100] = {};//城市之间的距离矩阵,来回相等
//选择算子
void Select(int start, int stop, individual *newpopulation){
int i, index;
double p, sum = 0.0;
double cfitness[POPSIZE];//个体适应度
//求总适应度sum
//#pragma omp parallel for
for (i = start; i < stop; i++){
sum += population[i].fitness;
}
//求每个个体的适应度所占的百分比cfitness[i]
#pragma omp parallel for
for (i = start; i < stop; i++){
cfitness[i] = population[i].fitness / sum;
}
//适应度求和,类似概率分布:0 1 2 3 4 -> 0 1 3 4 10
for (i = start + 1; i < stop; i++){
cfitness[i] = cfitness[i - 1] + cfitness[i];
}
//选择个体,
//#pragma omp parallel for
for (i = start; i < stop; i++){
p = rand() % 1000 / 1000.0;//0到1之间
index = 0;
while (p>cfitness[index]){
index++;
}
newpopulation[i] = population[index];
}
for (i = start; i < stop; i++){
population[i] = newpopulation[i];
}
}
void SelectionOperator(void){
individual newpopulation[POPSIZE];
int start = 0;
int stop = (int)PopSize / 3;
Select(start, stop, newpopulation);
start = stop + 1;
stop = (int)(2 * PopSize / 3);
Select(start, stop, newpopulation);
start = stop + 1;
stop = PopSize;
Select(start, stop, newpopulation);
}
//随机数生成
int random(int randmax){
return rand() % randmax;
}
//交叉算子
void CrossoverOperator(void){
//TODO:变量命名不规范,需要重新设计
int i, j;
int index[POPSIZE];
int point, temp;
double p;
int CityNo; //定义为染色体编码交换的 暂存BUFF
for (i = 0; i < PopSize; i++){
index[i] = i;//TODO:编号
}
//交换i 和 point+i 的编号
for (i = 0; i < PopSize; i++){
point = random(PopSize - i); //生成的随机数小于PopSize - i
temp = index[i];
index[i] = index[point + i];
index[point + i] = temp;
}
//交换 point 至 CHROMLENGTH 长度的 染色体编码
#pragma omp parallel for
for (i = 0; i < PopSize - 1; i += 2)
{
p = rand() % 1000 / 1000.0;
if (p < Pc){
point = random(CHROMLENGTH - 1) + 1;
for (j = point; j < CHROMLENGTH; j++){
CityNo = population[index[i]].chrom[j];
population[index[i]].chrom[j] = population[index[i + 1]].chrom[j];
population[index[i + 1]].chrom[j] = CityNo;
}
}
}
}
//变异算子
void MutationOperator(){
int i, j;
double p;
for (i = 0; i < PopSize; i++){
for (j = 1; j < CHROMLENGTH; j++){// 染色体第一个元素为1,不能变异
p = rand() % 1000 / 1000.0;
//随机修改基因
if (p < Pm){
population[i].chrom[j] = random(CITYCOUNT - j) + 1;
}
}
}
}
//生成初始群体
void GenerateInitialPopulation(){
int i, j;
for (i = 0; i < PopSize; i++){
CitySet[i] = i + 1;
}
srand((unsigned)time(NULL));
for (i = 0; i < PopSize; i++){
//出发城市固定为第一个城市
population[i].chrom[0] = 1;
for (j = 1; j < CITYCOUNT; j++){
population[i].chrom[j] = random(CITYCOUNT - j) + 1;
//std::cout << population[i].chrom[j];
}
}
}
//生成下一代群体
void GenerateNextPopulation(){
SelectionOperator();
CrossoverOperator();
MutationOperator();
}
//从城市序列中删去一个体,删除i个体
void DeleteOneCity(int *workset, int position, int n){
for (int i = position - 1; i < n - 1; i++){
workset[i] = workset[i + 1];
}
}
//对染色体Chrom解码,结果在Result中
void DecodeChromosome(int *chrom, int *Result){
int *p; //指向将要访问的城市
int i = 0;
int n = CITYCOUNT;
p = chrom;//出发城市
//初始工作城市数组
for (i = 0; i < CITYCOUNT; i++){
WorkSet[i] = CitySet[i];
}
//TODO:
for (i = 0; i < CITYCOUNT; i++){
Result[i] = WorkSet[*p - 1];//工作数组中第*p个元素是即将访问的城市
DeleteOneCity(WorkSet, *p, n);//从未访问的n个城市中删去刚访问的第*p个
n--;//未访问城市个数减1
p++;//即将访问城市指向下一个
}
}
//输出结果
void OutputTextReport(){
int i = 0;
DecodeChromosome(currentbest.chrom, TempCitySet);
printf("群体代数:%d , 最短路径: %f \n", generation, currentbest.value);
printf("个体编码:[");
for (i = 0; i < CHROMLENGTH; i++){
printf(" %d", currentbest.chrom[i]);
}
printf("]\n");
printf("访问顺序:[");
for (i = 0; i < CHROMLENGTH; i++){
printf(" %d", TempCitySet[i]);
}
printf("]\n\n");
}
//计算适应度
void CalculateFitness(){
int i, j;
double TotalDistance = 0.0;
//对每个个体,求其路径总长度
for (i = 0; i < PopSize; i++){
DecodeChromosome(population[i].chrom, TempCitySet);
for (j = 1; j < CITYCOUNT; j++){
TotalDistance = TotalDistance + CityDistance[TempCitySet[j - 1] - 1][TempCitySet[j] - 1];
}
TotalDistance = TotalDistance + CityDistance[TempCitySet[CITYCOUNT - 1] - 1][0];
population[i].value = TotalDistance;
TotalDistance = 0.0;
}
//城市数目 除以 个体路径总长度 = 个体适应度
//路径越短,适应度越高
for (i = 0; i < PopSize; i++){
population[i].fitness = CITYCOUNT / population[i].value;
}
}
//寻找最优个体
void FindBestIndividual(void)
{
int i;
bestindividual = population[0];
//通过比较适应度来筛选,越大越好
for (i = 1; i<PopSize; i++)
{
if (population[i].fitness>bestindividual.fitness)
bestindividual = population[i];
}
//对第一代
if (generation == 0)
{
currentbest = bestindividual;
}
else
{
if (bestindividual.fitness>currentbest.fitness)
{
currentbest = bestindividual;
OutputTextReport(); /* 每更新一次当前最优值,输出结果 */
//灾变控制清零
//disaster_control = 0;
//Pc = c ;
//Pm = m ;
}
//else{
// disaster_control++;
// if (disaster_control > 50){
// Pc = disaster_Pc;
// Pm = disaster_Pm;
// }
//}
}
}
//距离计算公式
int CalculatorEuler(int *src1, int *src2){
return sqrt(pow((src1[X] - src2[X]), 2) + pow((src1[Y] - src2[Y]), 2));
}
//计算两两城市之间的距离
void CalculatorDistance(){
int i, j;
for (i = 0; i<CITYCOUNT; i++){
for (j = 0; j<CITYCOUNT; j++){
CityDistance[i][j] = CalculatorEuler(city[i], city[j]);
//CityDistance[i][j] = 100;
//printf(" %d", CityDistance[i][j]);
}
//printf("\n");
}
}
//结果图形化显示:出发城市-绿色 路线-红色 城市-蓝色
void ShowResult(){
//cvZero(tsp_image);
tsp_image = cv::imread("china.jpg");
cv::rectangle(
tsp_image,
cv::Point(city[0][X] - 5, city[0][Y] - 5),
//cvPoint(box.x+box.width,box.y+box.height),
cv::Point(city[0][X] + 5, city[0][Y] + 5),
cv::Scalar(0, 0,255), //绿色
2
);
#pragma omp parallel for
for (int i = 1; i < CITYCOUNT; i++){
cv::rectangle(
tsp_image,
cv::Point(city[i][X] - 5, city[i][Y] - 5),
//cvPoint(box.x+box.width,box.y+box.height),
cv::Point(city[i][X] + 5, city[i][Y] + 5),
cv::Scalar(0, 0, 255),
2
);//
}
#pragma omp parallel for
for (int i = 0; i < CITYCOUNT - 1; i++){
cv::line(tsp_image, cv::Point(city[TempCitySet[i] - 1][X], city[TempCitySet[i] - 1][Y]), cv::Point(city[TempCitySet[i + 1] - 1][X], city[TempCitySet[i + 1] - 1][Y]), line, 2);
if ((i + 1) == (CITYCOUNT - 1)){
cv::line(tsp_image, cv::Point(city[TempCitySet[i + 1] - 1][X], city[TempCitySet[i + 1] - 1][Y]), cv::Point(city[0][X], city[0][Y]), line, 2);
}
}
cv::imshow("遗传算法解决旅行商问题仿真", tsp_image);
cv::waitKey(33);
}
int fileWrite()
{
int icount = pointcount;
fprintf(f, "pointcount:%d ", pointcount);
while (icount--)
fprintf(f, "{%d,%d},", city[icount][0], city[icount][1]);
fclose(f);
return 1;
}
int fileRead(){
int icount = 0;
char buff=',';
while (!feof(f)){
fscanf(f, "%f%d%f%d%f ", buff, city[icount][0], buff, city[icount][1],buff);
icount++;
}
fclose(f);
pointcount = icount;
return 1;
}