C++蚁群算法
#include<iostream>#include<string>
#include<math.h>
#include<fstream>
#include<vector>
#include<map>
#include<unordered_map>
#include<algorithm>
#include<conio.h>
using namespace std;
#pragma region 变量与常量的定义
//数据文件存放的路径
constexpr auto FILE_PATH = "D:\\data.txt";
//城市数量
const int CITY_NUMBER = 19;
//蚂蚁数量(一般为城市数量的1.5倍)
const int ANT_NUMBER = 50;
//最大迭代次数
const intMAX_ITERATIONS_NUMBER = 150;
//路径数量
const intPATH_NUMBER = 74;
//启发因子,信息素浓度重要性
const double ALPHA = 1.0;
//启发式重要性
const double BETA = 2.0;
//蚂蚁所携带总的信息素
const double Q = 100.0;
//信息素蒸发系数
const double ROU = 0.5;
//两两城市之间的信息素矩阵
vector<vector<double>> pheromoneMatrix;
//两两城市之间的距离
vector<vector<double>> cityDistance;
//下一代的信息素矩阵
vector<vector<double>> nextPheromoneMatrix;
//启发信息矩阵=1/cityDistance
vector<vector<double>> inspMartix;
//关键字:string类型,值:int类型。分别对应保存每次迭代的最优路径和对应的路径距离。
map<string, int>routResult;
//含有充电设施的站点编号
vector<int>rechargeNumber = { 3,4,7,8,12,14,16 };
#pragma endregion
#pragma region 蚂蚁
class Ant {
public:
//蚂蚁的当前位置
int antLoc;
//蚂蚁的禁忌表,存放已访问过的点
int taBu;
//蚂蚁走过路径的点的集合
int antPath;
//判断蚂蚁是否已经到达终点。True:表示到达,FALSE:表示未到达。
bool flag;
};
//初始化蚁群,大小为ANT_NUMBER
Ant antColony;
#pragma endregion
#pragma region 城市
class City {
public:
//城市编号
int cityNum;
//选择每个点的概率
double cityProb;
};
//初始化城市
City cityArray;
//线性化选择概率,用于轮盘赌算法
double lineCityProb;
#pragma endregion
#pragma region 矩阵初始化功能函数
//res:表示待初始化的矩阵
//temp:表示矩阵初始化的值
void initMatrix(vector<vector<double>> &res, int temp) {
//定义中间变量v
vector<double>v;
for (int i = 0; i < CITY_NUMBER; ++i) {
//清空v
vector<double>().swap(v);
for (int j = 0; j < CITY_NUMBER; ++j) {
v.push_back(temp);
}
res.push_back(v);
}
}
#pragma endregion
#pragma region 返回指定范围内的随机数
/// 返回指定范围内的随机浮点数
/// @param dbLow 范围起始数
/// @param dbUpper范围终止数
/// @return 返回的此范围内的随机数
double randomNumber(double dbLow, double dbUpper)
{
double dbTemp = rand() / ((double)RAND_MAX + 1.0);
return dbLow + dbTemp * (dbUpper - dbLow);
}
#pragma endregion
#pragma region 蚁群初始化
void initAntsColony() {
//初始化禁忌表和行走路线
for (int i = 0; i < ANT_NUMBER; ++i) {
for (int j = 0; j < CITY_NUMBER; ++j) {
//初始化蚁群的禁忌表,设置每只蚂蚁的禁忌表初始化为-1,代表从未访问任何点
antColony.taBu = -1;
}
for (int j = 0; j < PATH_NUMBER; ++j) {
//初始化蚁群的行走路径,设置每只蚂蚁的路径初始化为-1,代表还未经过任何点
antColony.antPath = -1;
}
}
//将蚂蚁放入初始位置,此处设置每只蚂蚁的出发点为0,代表从源点出发
for (int i = 0; i < ANT_NUMBER; ++i) {
//设置蚂蚁的当前位置为出发位置0
antColony.antLoc = 0;
//将当前位置更新到每只蚂蚁的禁忌表中,代表已经访问过此点
antColony.taBu = 0;
//将当前位置更新到每只蚂蚁的行走路径中,代表路径中已有此点
antColony.antPath = 0;
//将每只蚂蚁的flag设置为0,代表每只蚂蚁都还未到达终点
antColony.flag = 0;
}
}
#pragma endregion
#pragma region 初始化城市
void initCity() {
for (int i = 0; i < CITY_NUMBER; ++i) {
//初始化城市结构体
//初始化每个城市的编号为-1
cityArray.cityNum = -1;
//初始化每个城市的选择概率为0
cityArray.cityProb = 0;
//初始化线性选择概率为0
lineCityProb = 0;
}
}
#pragma endregion
#pragma region 初始化所需的各种矩阵,并读取文件距离数据
void initVariousMatrix() {
//初始化城市距离矩阵为-1
initMatrix(cityDistance, -1);
/* 数据文件的读取 */
//创建文件流对象
ifstream CityFile;
//打开文件
CityFile.open(FILE_PATH, ios::in);
//如果文件打开失败,则退出
if (!CityFile.is_open()) {
cout << "Open file failure" << endl;
exit(0);
}
//从文件中读取数据到城市距离矩阵之中
for (int i = 0; i < CITY_NUMBER; ++i) {
for (int j = 0; j < CITY_NUMBER; ++j) {
CityFile >> cityDistance;
}
}
//关闭文件
CityFile.close();
//初始化信息素矩阵为0
initMatrix(pheromoneMatrix, 0);
//初始化下一代信息素矩阵为0
initMatrix(nextPheromoneMatrix, 0);
//初始化启发信息矩阵为0
initMatrix(inspMartix, 0);
for (int i = 0; i < CITY_NUMBER; ++i) {
for (int j = 0; j < CITY_NUMBER; ++j) {
if (cityDistance != -1) { //如果两个城市之间有距离
inspMartix = 1 / cityDistance;//启发信息为两两城市距离的倒数
pheromoneMatrix = 1; //路径上信息素浓度初始值为1
}
}
}
}
#pragma endregion
#pragma region 判断城市j是否在蚂蚁k的禁忌表中
bool ifCityInTabu(int k, int j) {
for (int i = 0; i < CITY_NUMBER; ++i) {
//如果城市j,已经在蚂蚁k的禁忌表中,则返回1
if (j == antColony.taBu) {
return 1;
}
}
//如果不在蚂蚁k的禁忌表中,则返回0
return 0;
}
#pragma endregion
#pragma region 蚂蚁k从当前城市i选择下一步行进的城市j的概率
double nextCitySelProb(int k, int j) {
//初始化选择概率公式的分子
double p = 0;
//初始化选择概率公式的分母
double sum = 0;
//i保存蚂蚁k的当前位置
int i = antColony.antLoc;
//计算P
p = pow(pheromoneMatrix, ALPHA) * pow(inspMartix, BETA);
//计算sum
for (int m = 0; m < CITY_NUMBER; ++m) {
if (cityDistance != -1 && !ifCityInTabu(k, m)) {
sum += pow(pheromoneMatrix, ALPHA)*pow(inspMartix, BETA);
}
}
//返回从i--j的选择概率
return (p / sum);
}
#pragma endregion
#pragma region 更新蚂蚁k的禁忌表信息
void updateAntTabu(int k, int j) {
//蚂蚁k的当前位置赋值为j
antColony.antLoc = j;
for (int i = 0; i < CITY_NUMBER; ++i) {
//如果蚂蚁k还未访问过j,则将蚂蚁k的禁忌表信息添加j
if (antColony.taBu == -1) {
antColony.taBu = j;
break;
}
}
//同理,将蚂蚁k的行走路径中更新一个j
for (int i = 0; i < PATH_NUMBER; ++i) {
if (antColony.antPath == -1) {
antColony.antPath = j;
break;
}
}
}
#pragma endregion
#pragma region 轮盘赌选择下一步行进的城市
//蚂蚁k,选择前进的地点j的概率
int nextCitySelect(int k, int f) {
//记录蚂蚁可行进的城市个数,用于后面的计算
int c = 0;
//step 1:计算可行进的各城市的选择概率
for (int m = 0; m < CITY_NUMBER; ++m) {
//若城市(i,j)之间有路且j不在蚂蚁k的禁忌表中,则计算概率
if (cityDistance.antLoc] != -1 && !ifCityInTabu(k, m)) {
//对应的城市编号存入数组中
cityArray.cityNum = m;
//选择概率存入数组中
cityArray.cityProb = nextCitySelProb(k, m);
++c;
}
}
//step 2:线性化选择概率
for (int m = 0; m < c; ++m) {
for (int n = m; n >= 0; n--) {
//用于轮盘赌算法,将选择概率线性化累加
lineCityProb += cityArray.cityProb;
}
}
//step 3 产生随机数选择城市
//产生随机浮点数,范围0-1
double r = randomNumber(0, 1);
//选取的目标城市
int j = 0;
for (int m = 0; m < CITY_NUMBER; ++m) {
if (r <= lineCityProb) {
//将当前选择前进的城市编号赋值给j
j = cityArray.cityNum;
//将地点j更新至蚂蚁k的禁忌表中,代表已经访问过此点
updateAntTabu(k, j);
if (j == f) {
//若蚂蚁k下一步城市为目的地城市,则修改标志为1
antColony.flag = 1;
}
//return j;
return 0;
}
}
//return f;
return 0;
}
#pragma endregion
#pragma region 计算路径长度
int getAntLen(Ant ant) {
//定义路径长度为0
int len = 0;
for (int i = 0; i < PATH_NUMBER - 1; ++i) {
//如果蚂蚁行走的路径中有-1,表示未走过此点,则退出循环
if (ant.antPath == -1 || ant.antPath == -1)
break;
//若走过有路径,则不断累加距离结果
else
len += cityDistance]];
}
//返回路径长度
return len;
}
#pragma endregion
#pragma region 计算最优路径对应的蚂蚁编号
int getBestPathAntNumber() {
//初始化数组d为-1,保存所有蚂蚁的行走路径
vector<int>d(ANT_NUMBER, -1);
//假设定义蚂蚁k的路线到达目的地节点最短,即走过的路径为最短路径
int k = 0;
//将所有路径距离存入d中
for (int i = 0; i < ANT_NUMBER; i++)
{
d.push_back(getAntLen(antColony));
}
//定义指向最小路径的迭代器
auto min = min_element(d.begin(), d.end());
//计算此最短路径所对应的下标,此为蚂蚁的编号k
k = distance(d.begin(), min);
vector<int>(d).swap(d);
//返回蚂蚁k
return k;
}
#pragma endregion
#pragma region 打印最优路径所对应的蚂蚁K的路径和距离
void printBestPath(int k, int f) {
//初始化变量res,存放路线
string res = "";
cout << " 最短路径为:";
for (int i = 0; i < PATH_NUMBER - 1; ++i) {
//如果蚂蚁k的第i条路径为-1,表示没有走过此点,退出循环
if (antColony.antPath == -1)
break;
//打印蚂蚁k的第i条路径
cout << antColony.antPath;
//将路径转化为字符串形式存入res中,方便打印输出
res += to_string(antColony.antPath);
//若蚂蚁k的i+1条路径不为-1,代表访问过此点。即i--->i+1有路线。
if (antColony.antPath != -1) {
//打印"->"
cout << "->";
// 若此点含有充电设施
if (find(rechargeNumber.begin(), rechargeNumber.end(), antColony.antPath) != rechargeNumber.end()) {
res += "->(可充电站点)";
}
else {
res += "->";
}
}
}
cout << endl;
cout << " 对应距离为:" << getAntLen(antColony) << endl;
//把每次距离结果保存到map中:关键字:路线,值:对应的路径长度
routResult = getAntLen(antColony);
res.swap(res);
//return routResult;
}
#pragma endregion
#pragma region 更新信息素矩阵
void updatePherMartix() {
for (int i = 0; i < ANT_NUMBER; ++i) {
//全局更新信息素矩阵
if (antColony.flag == 1) {
for (int j = 0; j < PATH_NUMBER - 1; ++j) {
if (antColony.antPath == -1 || antColony.antPath == -1)
break;
else
nextPheromoneMatrix.antPath].antPath]
+= Q / getAntLen(antColony);
}
}
}
//更新下一代信息素矩阵
for (int i = 0; i < CITY_NUMBER; ++i) {
for (int j = 0; j < CITY_NUMBER; ++j) {
nextPheromoneMatrix =
(1 - ROU) * pheromoneMatrix + nextPheromoneMatrix;
}
}
}
#pragma endregion
#pragma region 迭代过程
void evolution(int city) {
cout << "开始进行迭代........." << endl;
//初始化参数
initAntsColony();
initCity();
initVariousMatrix();
int gen = 0;
while (gen < MAX_ITERATIONS_NUMBER) {
int p = 0;
while (p <11) {
for (int i = 0; i < ANT_NUMBER; ++i) {
if (antColony.flag == 1)
continue;
nextCitySelect(i, city);
initCity();
}
++p;
}
if (gen == MAX_ITERATIONS_NUMBER - 1) {
cout << " 迭代完成,输出结果" << endl;
printBestPath(getBestPathAntNumber(), city);
}
//更新信息素矩阵
updatePherMartix();
//蚁群初始化
initAntsColony();
pheromoneMatrix = nextPheromoneMatrix;
initMatrix(nextPheromoneMatrix, 0);
++gen;
}
}
#pragma endregion
#pragma region 释放vector内存空间,避免内存无限增加而崩溃
void deleArrary() {
vector<vector<double>>().swap(cityDistance);
vector<vector<double>>().swap(pheromoneMatrix);
vector<vector<double>>().swap(nextPheromoneMatrix);
vector<vector<double>>().swap(inspMartix);
//map<string, int>().swap(routResult);
}
#pragma endregion
#pragma region 按从小到大的顺序将vec内部value值进行排序,并打印出结果
void sortMap(map<string, int>t) {
//routeResult存入vec中,方便将结果进行排序
vector<pair<string, int>>vec;
for (const auto& n : t) {
vec.push_back(make_pair(n.first, n.second));
}
//将结果按照路径,从小到达进行排序
sort(vec.begin(), vec.end(), [](const pair<string, int>& x, const pair<string, int>& y)
-> int {
return x.second < y.second;
});
//输出排序好的所有结果
cout << "可供选择的路径中,从小到大为:" << endl;
int i = 1;
for (auto v : vec) {
cout << "第" << i << "条路径为: ";
cout << v.first << " " << "距离为:"
<< v.second << endl;
++i;
}
cout << endl;
cout << "建议采纳的最佳路线为:" << vec.first << " "
<< "此路线距离最短,耗时最小为:"
<< vec.second << endl;
for (auto v : vec)
{
if (vec.first.find("可充电站点") != -1) {
break;
}
else if (v.first.find("可充电站点") != -1) {
cout << "若需要中途停靠充电,建议采纳的最佳路线为:"
<< v.first
<< " 此路线含有充电设施,总的路径为:"
<< v.second
<< endl;
break;
}
}
t.swap(t);
vector<pair<string, int>>().swap(vec);
}
#pragma endregion
#pragma region 程序入口
int main() {
cout << " 请输入你要去往的城市:" << endl;
int city;
cin >> city;
//不断循环迭代,将每次循环的最优结果保存到routResult中
while (1) {
//随机化种子,用于生成随机数
srand((unsigned)time(NULL));
//开始进入迭代过程
evolution(city);
//每次迭代完成后,清空数组数据
deleArrary();
//判断键盘响应
if (_kbhit()) {
//如果读取到键盘按下'q'或'Q'键,则退出循环,结束整个过程
if (tolower(_getch()) == 'q') {
break;
}
}
}
//int x = 20;
//while (x) {
// //随机化种子,用于生成随机数
// srand((unsigned)time(NULL));
// //开始进入迭代过程
// evolution(city);
// //每次迭代完成后,清空数组数据
// deleArrary();
// --x;
//}
//将所有保存的结果按照从小到大进行排序输出,并打印出最佳结果
sortMap(routResult);
return 0;
}
#pragma endregion
页:
[1]