admin 发表于 2024-7-16 23:11:41

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]
查看完整版本: C++蚁群算法