为游客提供行程规划服务,在满足时间、资金约束下,使得行程的总体质量较高、路程较短。
上次采用遗传算法实现了这个功能,这次采用蚁群算法。
“一日游”的时间从早上八点到晚上七点;
生成的线路应尽量用完以上的游玩时间,但不能超时;
不需要考虑出发点和结束点,即可以从任意景点出发、在任意景点结束;
以线路中所有景点的评分的均值作为该线路的评价得分;
以所有景点的门票价格之和作为该线路的价格;
以各景点间的距离之和作为线路的路程长度;
线路的质量优劣,按以下公式计算:
Q = 0.4 ∗ n o r m a l ( 评价得分 ) + 0.4 ∗ n o r m a l ( 价格 ) + 0.2 ∗ n o r m a l ( 路程长度 ) Q=0.4 * normal(评价得分) + 0.4 * normal(价格)+0.2 * normal(路程长度) Q=0.4∗normal(评价得分)+0.4∗normal(价格)+0.2∗normal(路程长度)
其中, n o r m a l ( 评价得分 ) = 评价得分 景点数量 ; 0 ≤ 评价得分 ≤ 5 normal(评价得分) = \frac{评价得分}{景点数量}; 0\le评价得分\le5 normal(评价得分)=景点数量评价得分;0≤评价得分≤5
n o r m a l ( 价格 ) = 预算 − 价格 预算 ; 0 ≤ 价格 ≤ 预算 normal(价格)=\frac{预算-价格}{预算}; 0≤价格\le预算 normal(价格)=预算预算−价格;0≤价格≤预算
n o r m a l ( 路程长度 ) = 最大长度 − 路程长度 最大路程 ; 0 ≤ 路程长度 ≤ 最大长度 normal(路程长度)=\frac{最大长度-路程长度}{最大路程}; 0\le路程长度\le最大长度 normal(路程长度)=最大路程最大长度−路程长度;0≤路程长度≤最大长度
输入:
200,50//说明:200为游客的预算,则生成的线路的价格不能超过该预算;50为游客一天最远的行程,则线路的路程长度不能超过该最远行程。
输出:
//如果可行,则输出一条较优的线路:
景点1、景点3、景点8、景点4 (0.82、4.2、180、50)//说明:先按顺序输出景点名称,括号内分别为线路的质量得分、评价得分、价格、路程长度。
//如果不可行,如输入的预算为0且无免费景点,则输出:
error
蚁群算法(Ant Clony Optimization, ACO)是一种群智能算法,它是由一群无智能或有轻微智能的个体(Agent)通过相互协作而表现出智能行为,从而为求解复杂问题提供了一个新的可能性。蚁群算法最早是由意大利学者Colorni A., Dorigo M. 等于1991年提出。经过20多年的发展,蚁群算法在理论以及应用研究上已经得到巨大的进步。
蚁群算法是一种仿生学算法,是由自然界中蚂蚁觅食的行为而启发的。在自然界中,蚂蚁觅食过程中,蚁群总能够按照寻找到一条从蚁巢和食物源的最优路径。[1]
其中,z是所有剩余的可选择的节点, τ i j \tau_{ij} τij为第 i i i个节点到第 j j j个节点的路径上的信息素, η i j \eta_{ij} ηij为第 i i i个节点到第 j j j个节点的“能见度”(路程的倒数), α 和 β \alpha和\beta α和β分别为信息素和“能见度”的重要系数;
α \alpha α和 β \beta β影响蚁群的策略,需要一个适当的比例;
公式解释: 从当前节点 i i i到节点 j j j的概率为这两个节点间的信息素与能见度的乘积,占当前节点到所有可选择节点的比例。
本质上需要用到的信息与《「C++」遗传算法求解最佳路径问题——“一日游”行程规划程序》相似,所以大部分内容不需要修改。主要的差异在于实现算法的逻辑。
需要定义几个主要的类:
除此之外,还有一些数据类型和功能被反复用到,处理成单独的类和函数比较方便管理和计算:
class mytime
{
private:
int hour;
int min;
int sec;
public:
mytime() { this->hour = this->min = this->sec = 0; }
mytime(int h, int m = 0, int s = 0);
mytime(string t);
mytime(double t);
~mytime(){};
int H() const { return this->hour; }//返回小时
int M() const { return this->min; }//返回分钟
int S() const { return this->sec; }//返回秒
void H(int h) { this->hour = h; }//修改小时
void M(int m) { this->min = m; }//修改分钟
void S(int s) { this->sec = s; }//修改秒
int to_sec() const;//时间转为整数
mytime &operator=(const mytime &m);//重载赋值运算符
mytime &operator+=(const mytime &y);//重载自增运算符
friend mytime toMytime(int sec);//整型转为时间
friend ostream &operator<<(ostream &out, const mytime m);//重载输出运算符
//重载四则运算
friend mytime operator+(const mytime &m, const mytime &y);
friend mytime operator-(const mytime &m, const mytime &y);
friend double operator/(const mytime &m, const mytime &y);
friend double operator*(const int &m, const mytime &y);
//重载逻辑运算
friend bool operator>(const mytime &m, const mytime &y);
friend bool operator<(const mytime &m, const mytime &y);
friend bool operator>=(const mytime &m, const mytime &y);
friend bool operator<=(const mytime &m, const mytime &y);
friend bool operator==(const mytime &m, const mytime &y);
friend bool operator!=(const mytime &m, const mytime &y);
};
class coordinate
{
private:
float abscissa; // 横坐标
float ordinate; // 纵坐标
public:
coordinate() {}
coordinate(float x, float y);
coordinate(int x, int y);
coordinate(double x, double y);
float x() const { return abscissa; }//返回横坐标
float y() const { return ordinate; }//返回纵坐标
// 赋值
coordinate &operator=(const coordinate &coord);
// 计算距离
float distance(coordinate c);
friend ostream &operator<<(ostream &out, const coordinate c);
};
template <typename T>
void selection_sort(std::vector<T> &arr)
{
for (int i = 0; i < arr.size() - 1; i++)
{
int min = i;
for (int j = i + 1; j < arr.size(); j++)
if (arr[j] < arr[min])
min = j;
std::swap(arr[i], arr[min]);
}
}
// 查找
template <typename T>
int find_vector(vector<T> tlist, T t)
{
for (int i = 0; i < tlist.size(); i++)
{
if (tlist[i] == t)
{
return i;
}
}
return -1;
}
class scenicSpot
{
private:
string scenicname; // 景点名称
mytime busi_hour; // 营业时间
mytime clos_hour; // 歇业时间
float scenicprice; // 门票价格
float scenicscore; // 评分
mytime scenicduration; // 游玩时长
coordinate sceniccoord; // 景点坐标
public:
scenicSpot() {}
scenicSpot(string name, mytime busi = mytime(8), mytime clos = mytime(19), float price = 100, float score = 0, mytime duration = mytime(1), coordinate coord = coordinate(0, 0));
~scenicSpot(){};
// 返回各项成员
string name() const { return this->scenicname; }
mytime businesshour() const { return this->busi_hour; }
mytime closerhour() const { return this->clos_hour; }
float price() const { return this->scenicprice; }
float score() const { return this->scenicscore; }
mytime duration() const { return this->scenicduration; }
coordinate coord() const { return this->sceniccoord; }
// 重载运算符
scenicSpot &operator=(const scenicSpot s);
bool operator==(const scenicSpot &s);
friend ostream &operator<<(ostream &out, const scenicSpot &s);
};
class Ant
{
private:
vector<scenicSpot> nodes; // 基因序列,路线
double Antstar; // 路线评价
double Antdistance; // 路线路程
double Antprice; // 路线票价
mytime Antendtime; // 路线游玩时间
double Antscore; // 路线得分
public:
//略去不重要的函数
// 计算评分、路程等参数
void calculate();
// 其他操作
vector<vector<double>> deltaTau(); // 返回这只蚂蚁带来的flomone变化
bool isExist(scenicSpot spot) const;// 判断路线是否已经存在该景点
bool append();// 增加路线长度
double calcscore(double budget, double maxtrip);// 计算线路得分
// 重载算符
friend ostream &operator<<(ostream &out, const Ant &c);
Ant &operator=(const Ant s);
};
class antColony
{
private:
/* data */
vector<Ant> ants; // 蚂蚁
vector<Ant> best_ant; // 存储迭代中的最佳路径,防止好的结果被刷新掉(取20个)
vector<vector<double>> flomone; // 弗洛蒙浓度(信息素)
vector<scenicSpot> spots; // 景点信息
vector<vector<double>> spotsmap; // 景点地图(路程信息)
double bugdet; // 预算
double maxtrip; // 最大路程
int maxgen; // 最大迭代次数
ofstream fout; // 把过程写入文件方便查看
public:
//略去其他不重要的成员函数
int roulette(); // 轮盘赌
bool import(string path); // 从文件导入景点信息
bool calcDist();// 计算景点间距离
bool initFlomone(); // 初始化flomone
bool initAnt();// 初始化蚂蚁
bool updateFlomone(); // 更新Flomone
void Run();// 开始运行
};
#define Q 0.1 // 系统常数(取值任意)
#define rho 0.3 // 挥发系数
#define alpha 0.5 // flomone 重要程度系数
#define beta 1.5 // 能见度重要程度系数(此处能见度定义为路线得分)
#define tau 1 // flomone浓度初始值
#define ants_num 10// 一个起点放置的蚂蚁数量
#include "antColony.hpp"
int main(int argc, char *argv[])
{
antColony antcolony;
antcolony.Run();
return 0;
}
[1] 蚁群算法(Ant Colony Optimization)
[2] 【数之道 04】解决最优路径问题的妙招-蚁群ACO算法
[3] 「C++」遗传算法求解最佳路径问题——“一日游”行程规划程序