「C++」遗传算法求解最佳路径问题——“一日游”行程规划程序

1 题目描述

为游客提供行程规划服务,在满足时间、资金约束下,使得行程的总体质量较高、路程较短。

  • 某地有一组旅游景点,景点具有名称营业时间门票价格评分游玩时长、**地理位置(经纬度坐标)**等信息。
  • 该程序要根据游客的资金预算,自动生成一条合理的旅游路线。

2 题目要求

  1. “一日游”的时间从早上八点到晚上七点;

  2. 生成的线路应尽量用完以上的游玩时间,但不能超时;

  3. 不需要考虑出发点和结束点,即可以从任意景点出发、在任意景点结束;

  4. 以线路中所有景点的评分的均值作为该线路的评价得分

  5. 所有景点的门票价格之和作为该线路的价格

  6. 以各景点间的距离之和作为线路的路程长度

  7. 线路的质量优劣,按以下公式计算:
    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.4normal(评价得分)+0.4normal(价格)+0.2normal(路程长度)
    其中, 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路程长度最大长度

  8. 输入:

    20050//说明:200为游客的预算,则生成的线路的价格不能超过该预算;50为游客一天最远的行程,则线路的路程长度不能超过该最远行程。
    

    输出:

    //如果可行,则输出一条较优的线路:
    景点1、景点3、景点8、景点4 (0.824.218050)//说明:先按顺序输出景点名称,括号内分别为线路的质量得分、评价得分、价格、路程长度。
    //如果不可行,如输入的预算为0且无免费景点,则输出:
    error
    

3 解决方案

3.1 遗传算法

遗传算法(Genetic Algorithm,简称GA)起源于对生物系统所进行的计算机模拟研究,是一种随机全局搜索优化方法,它模拟了自然选择和遗传中发生的复制交叉变异等现象,从任一初始种群出发,通过随机选择、交叉和变异操作,产生一群更适合环境的个体,使群体进化到搜索空间中越来越好的区域,这样一代一代不断繁衍进化,最后收敛到一群最适应环境的个体(Individual),从而求得问题的优质解[1]

3.2 构思

3.2.1 核心定义的类

在C++中,内置的数据类型无法很好地满足我们的特殊需求,虽然STL容器拥有很强大且完备的功能,但是有些信息还是定义为类比较好处理,比如记录景点信息、路线信息、遗传信息等。需要定义几个主要的类:

  1. scenicSpot类用于记录一个景点的信息(景点名、开放时间、关闭时间、价格、坐标等);
  2. Chrom类用于记录条路线的信息(路线、路线评价、路线价格、路线路程等);
  3. Evolve类用于记录种群,并实现种群的进化方法。
3.2.2 常用类和方法

除此之外,还有一些数据类型和功能被反复用到,处理成单独的类和函数比较方便管理和计算:

  1. coordinate类,记录坐标,并实现坐标的计算功能;
  2. mytime类,记录时间,只记录时、分、秒,方便时间的运算;
  3. selector_sort函数,选择排序。
  4. find_vector函数,在vector中查找元素并返回下标。
  5. vector容器存储路线节点,方便变化长度,作为辅助作用
3.2.3 遗传算法的策略
  1. 精英遗传,当前一代的最优解之间相互杂交;
  2. 劣等淘汰,每一代最差的结果被淘汰;
  3. 优质选择,淘汰掉本代最差的结果后,从子代中选择最好的一批结果补充,以保持种群规模;

4 代码一览

4.1 mytime类

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);
};

4.2 coordinate类

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);
};

4.3 选择排序

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]);
    }
}

4.4 查找vector元素

// 查找
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;
}

4.5 scenicSpot类

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);
};

4.6 Chrom类

// 染色体
class Chrom
{
private:
    vector<scenicSpot> geneorder; // 基因序列,路线
    double chromstar;             // 路线评价
    double chromdistance;         // 路线路程
    double chromprice;            // 路线票价
    mytime chromendtime;          // 路线游玩时间
    double chromscore;            // 这条染色体的适应度
public:
    Chrom();
    Chrom(scenicSpot *gene);
    Chrom(vector<scenicSpot> gene);
    ~Chrom(){};
    // 计算参数‘
    void calculate();
    // 输出信息
    vector<scenicSpot> gene() const { return this->geneorder; }
    int size() const { return this->geneorder.size(); }
    double score() const { return this->chromscore; }                 // 返回得分
    double star() const { return this->chromstar; }                   // 返回路线评价
    double distance() const { return this->chromdistance; }           // 返回路线路程
    double price() const { return this->chromprice; }                 // 返回路线票价
    mytime endtime() const { return this->chromendtime; }             // 返回路线游玩时间
    coordinate end() const { return this->geneorder.back().coord(); } // 返回最后一站的坐标
    string route() const;                                             // 输出路线
    // 其他操作
    void append(scenicSpot s);                               // 增加路线长度
    vector<scenicSpot> getblock(int start, int end);         // 获取片段
    vector<scenicSpot> swapnodeAbarr();                      // 交换节点变异
    vector<scenicSpot> swapEpisodeAbarr();                   // 片段易位变异
    vector<scenicSpot> inverseEpisodeAbarr();                // 片段逆序变异
    void adjust(double budget, double maxtrip);              // 调整(如果超预算,删除尾部节点,以保证子代的有效性)
    Chrom onesideCross(vector<scenicSpot> cross, int start); // 杂交时的一边变换
    double calcscore(double budget, double maxtrip);         // 计算线路得分
    // 重载算符
    friend ostream &operator<<(ostream &out, const Chrom &c);
    Chrom &operator=(const Chrom s);
    bool operator<(const Chrom s);
    bool operator<=(const Chrom s);
    bool operator>(const Chrom s);
    bool operator>=(const Chrom s);
    bool operator!=(const Chrom s);
    bool operator==(const Chrom s);
};

4.7 Evolve类

// 遗传算法
class Evolve
{
private:
    vector<Chrom> parents;               // 父代
    vector<Chrom> children;              // 子代
    vector<scenicSpot> scenicmap;        // 景点地图(坐标)
    vector<vector<double>> distance_map; // 记录景点间的距离
    double maxtrip;                      // 路线的最大路程
    double budget;                       // 预算
    int maxgen;                          // 最大迭代次数
    int populat_scale;                   // 群体规模
    double cross_prob;                   // 交叉概率
    double abarr_prob;                   // 变异概率
    double select_prob;                  // 选择概率
    int children_scale;                  // 子代的选取个数
public:
    Evolve(int maxgen = 100, int pop_scale = 200, double crossp = 0.8, double abarrp = 0.0125, double slectp = 0.5, string path = "./map.csv");
    ~Evolve(){};
    bool importMap(string path); // 从文件导入景点地图
    bool calcdistMap();          // 计算路径
    void randomParents();        // 随机生成第一父代
    void Cross();                // 杂交
    void Aberrant();             // 变异(基因点交换、基因片段倒序、基因增长)
    void select();               // 选择子代插入父代
    bool evolveNow(); 			 // 开始演化
};

4.8 main函数

int main()
{
    Evolve evolution;
    evolution.evolveNow();
    return 0;
}
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
蚁群算法是一种模拟蚂蚁觅食行为的算法,用于解决路径规划问题。它适用于求解最短路径问题,包括求解最短路径和最优路径等。 下面以一个简单的案例来说明蚁群算法路径规划中的应用。 假设有一个城市地图,其中有多个城市之间的连接道路,我们需要找到从起点城市到终点城市的最短路径。 首先,我们需要将每个城市看作一个节点,并根据道路的距离来确定节点之间的边。然后,将一定数量的蚂蚁放置在起点城市,并让它们开始在城市间移动。 蚂蚁会根据一定的算法来选择下一个要访问的城市。通常情况下,蚂蚁更倾向于选择距离当前城市更近的城市,并且会受到已经被其他蚂蚁选择的路径的影响(信息素)。 当蚂蚁到达终点城市后,我们会评估它们所选择的路径长度,并根据路径长度来更新信息素。较短路径上的蚂蚁会释放更多的信息素,而较长路径上的蚂蚁会释放较少的信息素。这样,信息素会在城市间逐渐累积,进而影响其他蚂蚁的选择。 通过多轮迭代,蚂蚁们会逐渐找到一条最短路径,并且这条路径上的信息素含量会越来越高。最终,我们可以选择信息素含量最高的路径作为最优解,即最短路径。 需要注意的是,蚁群算法是一种启发式算法,其结果并不一定是全局最优解,但通常情况下能够找到较好的解决方案。 以上就是蚁群算法路径规划求解最短路径的一个简单案例。在实际应用中,蚁群算法还可以应用于其他领域的优化问题,如资源调度、旅行商问题等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

武的阶乘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值