车辆配送路径选择问题分析

目录

算法就是探索的过程嘛~,记录问题研究过程,不专业,其中可能有些错误~还望指正!

问题及数据

1. 问题说明

通过实际案例描述,根据配送点和业务需求量,进行最优路线的计算。由物流中心点出发,配送多个客户点后再回到起点,根据车辆数量,承载限制,不同车辆服务成本、运行里程限制等条件选择最优运输路径(总里程最短),使成本最小化,配送订单最大化,满载率最大化(如由一个配送中心向各个销售点配送货物,通过算法确定配送中心每辆车的配送方案,包括配送至哪个客户,配送量,下一个配送目的地)。

2. 测试数据和说明

某物流中心有5台配送车辆,车辆的最大载重均为8T,一次配送的最大行驶距离都为50KM,需要向20个客户送货,物流中心和20个客户的坐标及其客户的需求量随机产生,其中,物流中心的坐标为(14.2KM,13.1km),要求合理安排车辆的配送路线和载重量,使配送总里程最短。

3. 数据

客户点横坐标x(km)横坐标x(km)需求量q(t)
112.88.50.1
218.43.40.4
315.416.61.2
418.915.21.5
515.511.60.8
63.910.61.3
710.67.61.7
88.68.40.6
912.52.11.2
1013.85.20.4
116.716.90.9
1214.82.61.3
131.88.71.3
1417.1111.9
157.411.7
160.22.81.1
1711.919.81.5
1813.215.11.6
196.45.61.7
209.614.81.5

说明:各客户相互之间和物流中心与客户之间的距离均采用直线距离

思路一:贪心算法

1. 原始方案

[1] 过程分析

贪心算法其实很简单。
即每次选择距离最小的点,然后进行判断:

  1. 加入此点后,货车的剩余行驶里程能否从此点直接回到出发点。(此处可优化)
  2. 加入此点,是否造成载重不足情况。

如果都可以那么加入此点,寻找下一个距离最近的点,直到不满足条件回到出发点。
第一辆车回到出发点后,如果还存在未运输的城市,那么继续采用第二辆车以此类推,直到城市全部运输完成。

[2] 例子分析

用此方法的第一辆车做例子来讲解这个方法
在这里插入图片描述

计算与出发点距离最近的点,即5号点,那么进入5号点。进入完成后,再计算与5号点最近距离的点,以此类推。
计算所得:在这里插入图片描述

所得路线图如图所示
在这里插入图片描述

[3] 结果分析

贪心算法所取得的效果并不尽人意。
从每辆车的行驶距离和载重可以看出,大部分情况下都是载重先用完。
虽然一辆车的局部是最佳的但整体来说不是最佳的。

结果如图所示:
在这里插入图片描述
路线图:
在这里插入图片描述

[4] 案例代码
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

// 功能:将filename 中的数据(共cols列)读取到_vector中,_vector可视为二维数组
int read_scanf(const string &filename, const int &cols, vector<double *> &_vector)
{
    FILE *fp = fopen(filename.c_str(), "r");
    bool flag = true;
    int i = 0;
    if (!fp)
    {
        cout << "File open error!\n";
        return 0;
    }

    while (flag)
    {
        double *rowArray = new double[cols]; //new一个double类型的动态数组

        for (i = 0; i < cols; i++) //读取数据,存在_vector[cols]中
        {
            if (EOF == fscanf(fp,"%lf", &rowArray[i]))
            {
                flag = false;
                break;
            }
            //输出rowArray存入的数据
            //cout << rowArray[0] << " " << rowArray[1] << " " << rowArray[2] << " " << rowArray[3] << endl;
        }
        if (cols == i) //将txt文本文件中的一行数据存入rowArray中,并将rowArray存入vector中
            _vector.push_back(rowArray);
    }
    fclose(fp);
    return 1;
}

int  main()
{
    /*
    定义数据

    */
    double data_xy[21][2];//用来记录每个点的X,Y坐标。
    double data_q[21];//各点重量
    double data_dis[21][21];//各点到各点的距离
    int data_fin[21]= {0}; //是否走过此点
    int car_num=0;//车的数量
    double car_km[5];
    double car_kg[5];
    int car_pa[5][21];//这辆车经过的点
    int carlu[5]={1,1,1,1,1};

    data_xy[0][0]=14.2;
    data_xy[0][1]=13.1;
    data_fin[0]=1;

    /*
    读取数据
    */
    string file ="C:/Users/49786/Desktop/data.txt";
    //txt文件中有4列
    int columns = 4;
    vector<double *> output_vector;
    if (!read_scanf(file, columns, output_vector))
    {
        return 0;
    }
    //output_vector可视为二维数组;输出数组元素:
    int rows = output_vector.size();
    for (int i = 0; i < rows; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            if(j!=3)
            {
                data_xy[i+1][j-1]=output_vector[i][j];
            }
            else
            {
                data_q[i+1]=output_vector[i][j];
            }
        }
    }
    //cout<<data_xy[1][0]<<endl;
    //cout<<data_xy[1][1]<<endl;
   // cout<<data_q[1]<<endl;

    /*
     计算各个点之间的距离

    */

    for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_dis[i][j]=sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2));
        }
    }
/*
     for(int i=0;i<21;i++){
            cout<<i<<": ";
        for(int j=0;j<21;j++){
            cout<<data_dis[i][j]<<" ";
        }
        cout<<endl;
    }

*/



    for(int car=1; car<=5; car++)
    {

        double km=50;//最大行驶路程
        double kg=8;//最大载重
        int di=0;//下一步要走的地点。
        int ki=0;//记录下一步
        int chong=0;//避免重复
        car_pa[car-1][0]=di;
        for(int i=0; km>=0||kg>=0; i++)
        {
            double minum=100;
            for(int j=1; j<21;j++)
            {
                if(j!=di&&data_fin[j]!=1)
                {
                    if(data_dis[di][j]<minum)
                    {
                        minum=data_dis[di][j];
                        ki=j;
                        //cout<<"是否出现"<<data_fin[j]<<"  ";
                        //cout<<"找出最小值下标:"<<ki<<endl;
                        //cout<<"找出最小值:"<<minum<<endl;

                    }
                }//找出最小的数值以及下表
            }
            di=ki;

            //cout<<"距离:"<<km-data_dis[car_pa[car-1][i]][di]<<"回去的距离:"<<data_dis[di][0]<<endl;
            //cout<<"如果加入那么减去的重量"<<kg-data_q[di]<<endl;
            if((km-data_dis[car_pa[car-1][i]][di])>=data_dis[di][0]&&(kg-data_q[di])>=0)
            {

                if(chong==di){
                    //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
                }


                car_pa[car-1][i+1]=di;//下一步设置为di
                data_fin[di]=1;
                km-=data_dis[car_pa[car-1][i]][di];
                kg-=data_q[di];
                car_km[car-1]+=data_dis[car_pa[car-1][i]][di];
                car_kg[car-1]+=data_q[di];
                chong=di;
                carlu[car-1]++;
            }
            else
            {
                //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
            }


        }
        int k=1;

        for(int i=1;i<21;i++){
            if(data_fin[i]==0){
                k=0;
            }
        }

        if(k==1)break;

        car_num=car_num+1;

    }
    double carallkm=0;

    for(int i=0;i<=car_num;i++){


        cout<<"第"<<i+1<<"辆车路径:";
        for(int j=0;j<carlu[i];j++){

            cout<<car_pa[i][j]<<"-";
        }
        cout<<"行驶距离:"<<car_km[i]<<endl;
        cout<<"载重:"<<car_kg[i]<<endl;
        carallkm+=car_km[i];
    }

    cout<<"总距离"<<carallkm<<endl;


    return 0;
}

2. 改进尝试A-失败(╯▔皿▔)╯

a. 改进点讨论

在原始方案中,过程分析的第一点提到了,车辆在最后一个城市会直接返回到原点

改进
在到达最后一个城市后,不是直接返回原点,而是挑选能够到达并且到达后还能直接回到原点的城市,载重也必须够用。
因为加入这个新城市,还能够直接跳回原点的必然是相较于贪心选择的城市离原点更近。

只要能多完成一个城市,必然可以减少下一辆车的距离。从而从从整体上优化行车的距离。

判断条件
1.到达贪心最后一个城市后不再考虑距离,只考虑加入的这个城市后在能够直接回到原点(从符合条件的点中,选择离最后一个城市最近的【值得考虑】)。
2.载重够用。

b.例子分析

以第二辆车为例子:
在这里插入图片描述

在第二辆车到达9点时,按照正常计算应该计算 最近的12点是否符合条件,12点 行程足够而重量不足如黄色箭头所示,按第一种方法应该直接返回出发点。
而根据改进尝试,在到达9点到12点无法达到时,将不再考虑离其最近的路程点,而是考虑剩余的行程和载重合适的点,如蓝色所示

c. 结果分析

很好 反向操作,负优化 !!!(╯▔皿▔)╯

在这里插入图片描述
路线图:
在这里插入图片描述

c. 代码
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

// 功能:将filename 中的数据(共cols列)读取到_vector中,_vector可视为二维数组
int read_scanf(const string &filename, const int &cols, vector<double *> &_vector)
{
    FILE *fp = fopen(filename.c_str(), "r");
    bool flag = true;
    int i = 0;
    if (!fp)
    {
        cout << "File open error!\n";
        return 0;
    }

    while (flag)
    {
        double *rowArray = new double[cols]; //new一个double类型的动态数组

        for (i = 0; i < cols; i++) //读取数据,存在_vector[cols]中
        {
            if (EOF == fscanf(fp,"%lf", &rowArray[i]))
            {
                flag = false;
                break;
            }
            //输出rowArray存入的数据
            //cout << rowArray[0] << " " << rowArray[1] << " " << rowArray[2] << " " << rowArray[3] << endl;
        }
        if (cols == i) //将txt文本文件中的一行数据存入rowArray中,并将rowArray存入vector中
            _vector.push_back(rowArray);
    }
    fclose(fp);
    return 1;
}

int  main()
{
    /*
    定义数据

    */
    double data_xy[21][2];//用来记录每个点的X,Y坐标。
    double data_q[21];//各点重量
    double data_dis[21][21];//各点到各点的距离
    int data_fin[21]= {0}; //是否走过此点
    int car_num=0;//车的数量
    double car_km[5];
    double car_kg[5];
    int car_pa[5][21];//这辆车经过的点
    int carlu[5]={1,1,1,1,1};

    data_xy[0][0]=14.2;
    data_xy[0][1]=13.1;
    data_fin[0]=1;

    /*
    读取数据
    */
    string file ="C:/Users/49786/Desktop/data.txt";
    //txt文件中有4列
    int columns = 4;
    vector<double *> output_vector;
    if (!read_scanf(file, columns, output_vector))
    {
        return 0;
    }
    //output_vector可视为二维数组;输出数组元素:
    int rows = output_vector.size();
    for (int i = 0; i < rows; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            if(j!=3)
            {
                data_xy[i+1][j-1]=output_vector[i][j];
            }
            else
            {
                data_q[i+1]=output_vector[i][j];
            }
        }
    }
    //cout<<data_xy[1][0]<<endl;
    //cout<<data_xy[1][1]<<endl;
   // cout<<data_q[1]<<endl;

    /*
     计算各个点之间的距离

    */

    for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_dis[i][j]=sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2));
        }
    }
/*
     for(int i=0;i<21;i++){
            cout<<i<<": ";
        for(int j=0;j<21;j++){
            cout<<data_dis[i][j]<<" ";
        }
        cout<<endl;
    }

*/



    for(int car=1; car<=5; car++)
    {

        double km=50;//最大行驶路程
        double kg=8;//最大载重
        int di=0;//下一步要走的地点。
        int ki=0;//记录下一步
        int chong=0;//避免重复
        car_pa[car-1][0]=di;
        for(int i=0; km>=0||kg>=0; i++)
        {
            double minum=100;
            for(int j=1; j<21;j++)
            {
                if(j!=di&&data_fin[j]!=1)
                {
                    if(data_dis[di][j]<minum)
                    {
                        minum=data_dis[di][j];
                        ki=j;
                        //cout<<"是否出现"<<data_fin[j]<<"  ";
                        //cout<<"找出最小值下标:"<<ki<<endl;
                        //cout<<"找出最小值:"<<minum<<endl;

                    }
                }//找出最小的数值以及下表
            }
            di=ki;

            //cout<<"距离:"<<km-data_dis[car_pa[car-1][i]][di]<<"回去的距离:"<<data_dis[di][0]<<endl;
            //cout<<"如果加入那么减去的重量"<<kg-data_q[di]<<endl;
            if((km-data_dis[car_pa[car-1][i]][di])>=data_dis[di][0]&&(kg-data_q[di])>=0)
            {

                if(chong==di){
                    //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
                }


                car_pa[car-1][i+1]=di;//下一步设置为di
                data_fin[di]=1;
                km-=data_dis[car_pa[car-1][i]][di];
                kg-=data_q[di];
                car_km[car-1]+=data_dis[car_pa[car-1][i]][di];
                car_kg[car-1]+=data_q[di];
                chong=di;
                carlu[car-1]++;
            }
            else
            {
                double minuu=100;
                int uj=0;//记录当前
                for(int u=1;u<21;u++){
                    if(data_fin[u]!=1){
                        if((km-data_dis[car_pa[car-1][i]][u])>=data_dis[u][0]&&(kg-data_q[u])>=0&&data_dis[u][0]<=data_dis[car_pa[car-1][i]][0]){
                            if(data_dis[car_pa[car-1][i]][u]<minuu){
                                minuu=data_dis[car_pa[car-1][i]][u];
                                uj=u;
                            }
                        }
                    }
                }
                if(uj!=0){
                    car_pa[car-1][i+1]=uj;
                    data_fin[uj]=1;
                    km-=data_dis[car_pa[car-1][i]][uj];
                    kg-=data_q[uj];
                    car_km[car-1]+=data_dis[car_pa[car-1][i]][uj];
                    car_kg[car-1]+=data_q[uj];
                    carlu[car-1]++;
                }else{

                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;

                }

                //这一步大有可为!

            }


        }
        int k=1;

        for(int i=1;i<21;i++){
            if(data_fin[i]==0){
                k=0;
            }
        }

        if(k==1)break;

        car_num=car_num+1;

    }
    double carallkm=0;

    for(int i=0;i<=car_num;i++){


        cout<<"第"<<i+1<<"辆车路径:";
        for(int j=0;j<carlu[i];j++){

            cout<<car_pa[i][j]<<"-";
        }
        cout<<"行驶距离:"<<car_km[i]<<endl;
        cout<<"载重:"<<car_kg[i]<<endl;
        carallkm+=car_km[i];
    }

    cout<<"总距离"<<carallkm<<endl;


    return 0;
}

3. 改进尝试B-略有改进(注意:理论上貌似行不通)

a. 改进点讨论

在原始方案中,采取了选择最小距离的办法,那么我们引入权重,用权重来进行贪心算法(距离/重量 即选取每吨重量所用最少距离的地点)

改进
将原本的距离,变成 距离/重量,即选取每吨重量所用最小千米数的地点

判断条件
1.距离与重量的比值,选取最小的。
2.载重够用。

b.例子分析

其实在每个方案的计算中,我们都会拿到每个点到达每个点之间的距离表。现在我们要引入每个点到每个点的距离与重量的比值表,拿它作为贪心的依据:
在这里插入图片描述
数据暂不提供。
在这里插入图片描述

c. 结果分析

感觉还不错,最起码下降了一点(。・∀・)ノ

在这里插入图片描述
路线图:
在这里插入图片描述

c. 代码
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

// 功能:将filename 中的数据(共cols列)读取到_vector中,_vector可视为二维数组
int read_scanf(const string &filename, const int &cols, vector<double *> &_vector)
{
    FILE *fp = fopen(filename.c_str(), "r");
    bool flag = true;
    int i = 0;
    if (!fp)
    {
        cout << "File open error!\n";
        return 0;
    }

    while (flag)
    {
        double *rowArray = new double[cols]; //new一个double类型的动态数组

        for (i = 0; i < cols; i++) //读取数据,存在_vector[cols]中
        {
            if (EOF == fscanf(fp,"%lf", &rowArray[i]))
            {
                flag = false;
                break;
            }
            //输出rowArray存入的数据
            //cout << rowArray[0] << " " << rowArray[1] << " " << rowArray[2] << " " << rowArray[3] << endl;
        }
        if (cols == i) //将txt文本文件中的一行数据存入rowArray中,并将rowArray存入vector中
            _vector.push_back(rowArray);
    }
    fclose(fp);
    return 1;
}

int  main()
{
    /*
    定义数据

    */
    double data_xy[21][2];//用来记录每个点的X,Y坐标。
    double data_q[21];//各点重量
    double data_dis[21][21];//各点到各点的距离
    double data_qz[21][21];//各点到各点的权重
    int data_fin[21]= {0}; //是否走过此点
    int car_num=0;//车的数量
    double car_km[5];
    double car_kg[5];
    int car_pa[5][21];//这辆车经过的点
    int carlu[5]={1,1,1,1,1};

    data_xy[0][0]=14.2;
    data_xy[0][1]=13.1;
    data_fin[0]=1;

    /*
    读取数据
    */
    string file ="C:/Users/49786/Desktop/data.txt";
    //txt文件中有4列
    int columns = 4;
    vector<double *> output_vector;
    if (!read_scanf(file, columns, output_vector))
    {
        return 0;
    }
    //output_vector可视为二维数组;输出数组元素:
    int rows = output_vector.size();
    for (int i = 0; i < rows; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            if(j!=3)
            {
                data_xy[i+1][j-1]=output_vector[i][j];
            }
            else
            {
                data_q[i+1]=output_vector[i][j];
            }
        }
    }
    //cout<<data_xy[1][0]<<endl;
    //cout<<data_xy[1][1]<<endl;
   // cout<<data_q[1]<<endl;

    /*
     计算各个点之间的距离

    */

    for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_dis[i][j]=sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2));
        }
    }
     for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_qz[i][j]=data_dis[i][j]/data_q[j];
        }
    }
/*
     for(int i=0;i<21;i++){
            cout<<i<<": ";
        for(int j=0;j<21;j++){
            cout<<data_dis[i][j]<<" ";
        }
        cout<<endl;
    }

*/



    for(int car=1; car<=5; car++)
    {

        double km=50;//最大行驶路程
        double kg=8;//最大载重
        int di=0;//下一步要走的地点。
        int ki=0;//记录下一步
        int chong=0;//避免重复
        car_pa[car-1][0]=di;
        for(int i=0; km>=0||kg>=0; i++)
        {
            double minum=100;
            for(int j=1; j<21;j++)
            {
                if(j!=di&&data_fin[j]!=1)
                {
                    if(data_qz[di][j]<minum)
                    {
                        minum=data_qz[di][j];
                        ki=j;
                        //cout<<"是否出现"<<data_fin[j]<<"  ";
                        //cout<<"找出最小值下标:"<<ki<<endl;
                        //cout<<"找出最小值:"<<minum<<endl;

                    }
                }//找出最小的数值以及下表
            }
            di=ki;

            //cout<<"距离:"<<km-data_dis[car_pa[car-1][i]][di]<<"回去的距离:"<<data_dis[di][0]<<endl;
            //cout<<"如果加入那么减去的重量"<<kg-data_q[di]<<endl;
            if((km-data_dis[car_pa[car-1][i]][di])>=data_dis[di][0]&&(kg-data_q[di])>=0)
            {

                if(chong==di){
                    //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
                }


                car_pa[car-1][i+1]=di;//下一步设置为di
                data_fin[di]=1;
                km-=data_dis[car_pa[car-1][i]][di];
                kg-=data_q[di];
                car_km[car-1]+=data_dis[car_pa[car-1][i]][di];
                car_kg[car-1]+=data_q[di];
                chong=di;
                carlu[car-1]++;
            }
            else
            {
                //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
            }


        }
        int k=1;

        for(int i=1;i<21;i++){
            if(data_fin[i]==0){
                k=0;
            }
        }

        if(k==1)break;

        car_num=car_num+1;

    }
    double carallkm=0;

    for(int i=0;i<=car_num;i++){


        cout<<"第"<<i+1<<"辆车路径:";
        for(int j=0;j<carlu[i];j++){

            cout<<car_pa[i][j]<<"-";
        }
        cout<<"行驶距离:"<<car_km[i]<<endl;
        cout<<"载重:"<<car_kg[i]<<endl;
        carallkm+=car_km[i];
    }

    cout<<"总距离"<<carallkm<<endl;


    return 0;
}

4. 改进尝试C-在改进A中引入B的比值法

a. 改进点讨论

在改进A中引入B的比值来进行路线的查找。

改进
在尝试A的基础上,将原本的距离,变成 距离/重量,即选取每吨重量所用最小千米数的地点

判断条件
1.在尝试A的基础上距离与重量的比值,选取最小的。
2.载重够用。

b. 结果分析

略逊于B方案,相较于原始改进A 略强

在这里插入图片描述
路线图:
在这里插入图片描述

c. 代码

#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

// 功能:将filename 中的数据(共cols列)读取到_vector中,_vector可视为二维数组
int read_scanf(const string &filename, const int &cols, vector<double *> &_vector)
{
    FILE *fp = fopen(filename.c_str(), "r");
    bool flag = true;
    int i = 0;
    if (!fp)
    {
        cout << "File open error!\n";
        return 0;
    }

    while (flag)
    {
        double *rowArray = new double[cols]; //new一个double类型的动态数组

        for (i = 0; i < cols; i++) //读取数据,存在_vector[cols]中
        {
            if (EOF == fscanf(fp,"%lf", &rowArray[i]))
            {
                flag = false;
                break;
            }
            //输出rowArray存入的数据
            //cout << rowArray[0] << " " << rowArray[1] << " " << rowArray[2] << " " << rowArray[3] << endl;
        }
        if (cols == i) //将txt文本文件中的一行数据存入rowArray中,并将rowArray存入vector中
            _vector.push_back(rowArray);
    }
    fclose(fp);
    return 1;
}

int  main()
{
    /*
    定义数据

    */
    double data_xy[21][2];//用来记录每个点的X,Y坐标。
    double data_q[21];//各点重量
    double data_dis[21][21];//各点到各点的距离
    double data_qz[21][21];
    int data_fin[21]= {0}; //是否走过此点
    int car_num=0;//车的数量
    double car_km[5];
    double car_kg[5];
    int car_pa[5][21];//这辆车经过的点
    int carlu[5]={1,1,1,1,1};

    data_xy[0][0]=14.2;
    data_xy[0][1]=13.1;
    data_fin[0]=1;

    /*
    读取数据
    */
    string file ="C:/Users/49786/Desktop/data.txt";
    //txt文件中有4列
    int columns = 4;
    vector<double *> output_vector;
    if (!read_scanf(file, columns, output_vector))
    {
        return 0;
    }
    //output_vector可视为二维数组;输出数组元素:
    int rows = output_vector.size();
    for (int i = 0; i < rows; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            if(j!=3)
            {
                data_xy[i+1][j-1]=output_vector[i][j];
            }
            else
            {
                data_q[i+1]=output_vector[i][j];
            }
        }
    }
    //cout<<data_xy[1][0]<<endl;
    //cout<<data_xy[1][1]<<endl;
   // cout<<data_q[1]<<endl;

    /*
     计算各个点之间的距离

    */

    for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_dis[i][j]=sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2));
        }
    }

    for(int i=0; i<21; i++)
    {   //cout<<i<<": "<<endl;
        for(int j=0; j<21; j++)
        {   //cout<<j<<"---"<<endl;
            //cout<<"x相减:"<<data_xy[i][0]-data_xy[j][0]<<"平方:"<<pow(data_xy[i][0]-data_xy[j][0],2)<<endl;
           // cout<<"y相减:"<<data_xy[i][1]-data_xy[j][1]<<"平方:"<<pow(data_xy[i][1]-data_xy[j][1],2)<<endl;
            //cout<<"相加开方"<<sqrt(pow(data_xy[i][0]-data_xy[j][0],2)+pow(data_xy[i][1]-data_xy[j][1],2))<<endl;
            data_qz[i][j]=data_dis[i][j]/data_q[j];
        }
    }
/*
     for(int i=0;i<21;i++){
            cout<<i<<": ";
        for(int j=0;j<21;j++){
            cout<<data_dis[i][j]<<" ";
        }
        cout<<endl;
    }

*/



    for(int car=1; car<=5; car++)
    {

        double km=50;//最大行驶路程
        double kg=8;//最大载重
        int di=0;//下一步要走的地点。
        int ki=0;//记录下一步
        int chong=0;//避免重复
        car_pa[car-1][0]=di;
        for(int i=0; km>=0||kg>=0; i++)
        {
            double minum=100;
            for(int j=1; j<21;j++)
            {
                if(j!=di&&data_fin[j]!=1)
                {
                    if(data_qz[di][j]<minum)
                    {
                        minum=data_qz[di][j];
                        ki=j;
                        //cout<<"是否出现"<<data_fin[j]<<"  ";
                        //cout<<"找出最小值下标:"<<ki<<endl;
                        //cout<<"找出最小值:"<<minum<<endl;

                    }
                }//找出最小的数值以及下表
            }
            di=ki;

            //cout<<"距离:"<<km-data_dis[car_pa[car-1][i]][di]<<"回去的距离:"<<data_dis[di][0]<<endl;
            //cout<<"如果加入那么减去的重量"<<kg-data_q[di]<<endl;
            if((km-data_dis[car_pa[car-1][i]][di])>=data_dis[di][0]&&(kg-data_q[di])>=0)
            {

                if(chong==di){
                    //这一步大有可为!
                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;
                }


                car_pa[car-1][i+1]=di;//下一步设置为di
                data_fin[di]=1;
                km-=data_dis[car_pa[car-1][i]][di];
                kg-=data_q[di];
                car_km[car-1]+=data_dis[car_pa[car-1][i]][di];
                car_kg[car-1]+=data_q[di];
                chong=di;
                carlu[car-1]++;
            }
            else
            {
                double minuu=100;
                int uj=0;//记录当前
                for(int u=1;u<21;u++){
                    if(data_fin[u]!=1){
                        if((km-data_dis[car_pa[car-1][i]][u])>=data_dis[u][0]&&(kg-data_q[u])>=0&&data_dis[u][0]<=data_dis[car_pa[car-1][i]][0]){
                            if(data_dis[car_pa[car-1][i]][u]<minuu){
                                minuu=data_dis[car_pa[car-1][i]][u];
                                uj=u;
                            }
                        }
                    }
                }
                if(uj!=0){
                    car_pa[car-1][i+1]=uj;
                    data_fin[uj]=1;
                    km-=data_dis[car_pa[car-1][i]][uj];
                    kg-=data_q[uj];
                    car_km[car-1]+=data_dis[car_pa[car-1][i]][uj];
                    car_kg[car-1]+=data_q[uj];
                    carlu[car-1]++;
                }else{

                car_pa[car-1][i+1]=0;
                car_km[car-1]+=data_dis[car_pa[car-1][i]][0];
                carlu[car-1]++;
                break;

                }

                //这一步大有可为!

            }


        }
        int k=1;

        for(int i=1;i<21;i++){
            if(data_fin[i]==0){
                k=0;
            }
        }

        if(k==1)break;

        car_num=car_num+1;

    }
    double carallkm=0;

    for(int i=0;i<=car_num;i++){


        cout<<"第"<<i+1<<"辆车路径:";
        for(int j=0;j<carlu[i];j++){

            cout<<car_pa[i][j]<<"-";
        }
        cout<<"行驶距离:"<<car_km[i]<<endl;
        cout<<"载重:"<<car_kg[i]<<endl;
        carallkm+=car_km[i];
    }

    cout<<"总距离"<<carallkm<<endl;


    return 0;
}

思路二:暴力求解

1.思路分析

对于运输的路径,我们采取随机的方式生成,也就是将20个点随机的排列组合,然后进行计算。

整体上思路比较简单,但是需要花费的时间较多。

2.过程分析

首先会随机生成一个序列,从出发点,顺着此序列进行计算,当不满足条件时回到出发点,换下一辆车继续计算,知道路径走完或者车走完为止。

例如:
在这里插入图片描述
注意!!!
这张表格只是为了方便向大家展示随机生成的序列,我们每次都是随机生成一条的,并不是生成一张表格,因为这样的话,消耗太大,也没有必要。

每个序列完成后,都会记录其所需要的路程和其路径。

3.判断条件

1.加入此点后,能否直接回到出发点,如果不能那么回到出发点,换下一辆车(贪心算法原始方案相同)直到计算完成整个序列,记录其值和路径。

2.记录当前获取的总路径的最小值,如果序列在未走完时所消耗的路程已经大于当前总路径的最小值,那么直接舍弃,重新生成新的序列计算。

3.载重够用。

4.结果分析

暴力求解结果过于随机,理论上只要无限跑总能找到最优的解。

并且因为是随机生成的序列,所以理论上还是看运气。

就像下面一样两亿次还不如一亿次(╯▔皿▔)╯

下面是分别跑了 1亿次 和 2亿次的结果和路径图:

  • 1亿次得出的最小的总路径
路径:第一辆车:0-6-20-11-17-4-3-0
	 第二辆车:0-5-1-15-19-16-13-8-0
	 第三辆车:0-2-9-12-7-10-14-0
	 第四辆车:0-18-0
	 
总里程:134.39KM

路径图:
在这里插入图片描述

  • 2亿次得出的最小的总路径
路径:第一辆车:0-18-8-6-13-11-17-0
	 第二辆车:0-20-19-16-15-1-10-5-0
	 第三辆车:0-4-2-12-9-7-14-0
	 第四辆车:0-3-0
	 
总里程:138.09KM

路径图:
在这里插入图片描述

5.代码


思路三:智能算法

代码

https://download.csdn.net/download/jnbfknasf113/18908669?spm=1001.2014.3001.5503

1.禁忌算法

(1) 禁忌算法基本思路

1.选定一个初始解𝑋_𝑛𝑜𝑤;令禁忌表𝐻=∅ 。

  1. 若满足终止准则,则转第四步;否则,在𝑋_𝑛𝑜𝑤的邻域𝑁(𝑋_𝑛𝑜𝑤)中选出满足禁忌要求的候选集𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤),转第三步。

  2. 在𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤)中选一个评价值最好的解𝑋_𝑏𝑒𝑠𝑡,令𝑋_𝑛𝑜𝑤=𝑋_𝑏𝑒𝑠𝑡,更新禁忌表H,转第二步。

  3. 输出计算结果,停止。

(2)禁忌算法伪代码

在这里插入图片描述

(3)禁忌算法具体实现细节

初始化:随机产生一个可行路径作为初始解,将其设置为最优路径。清空禁忌表,设置禁忌长度(5, 10, 15)。

邻域搜索产生候选解:邻域函数(交换两个元素)。我们探测该解的全邻域,即一共190个解(19+18+ … +2+1),将其中满足限制条件(重量、距离)且未被禁忌的解挑选出来作为邻域。

选择最好的候选解:在邻域中选出适应度(距离最短)最好的候选解,并将其与当前最优值进行比较。如果优于当前最佳解,则更新最佳解。更新禁忌表(禁忌该操作方式,并将其他被禁方式禁忌长度减1),将该最好的候选解作为下一轮的种子。

判断终止条件: 迭代是否达到200次。

(4)禁忌算法算法结果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.模拟退火算法

(1) 模拟退火算法基本思路

1.选定一个初始解𝑋_𝑛𝑜𝑤;设定初始温度𝑇_s。

  1. 若满足终止准则,则转第四步;否则,在𝑋_𝑛𝑜𝑤的邻域𝑁(𝑋_𝑛𝑜𝑤 ) 中选出候选集𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤),转第三步。

  2. 在𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤)中选一个评价值最好的解𝑋_𝑏𝑒𝑠𝑡。如果𝑋_best优于𝑋_(𝑐𝑢𝑟_𝑏𝑒𝑠𝑡),则令𝑋_𝑛𝑜𝑤=𝑋_𝑏𝑒𝑠𝑡,并且更新𝑋_(𝑐𝑢𝑟_𝑏𝑒𝑠𝑡);否则,以一定的概率p接受𝑋_𝑏𝑒𝑠𝑡 作为下一轮的种子。降温,转第二步。

  3. 输出计算结果,停止。

(2)模拟退火算法伪代码

在这里插入图片描述

(3)模拟退火算法具体实现细节

初始化: 随机产生一个可行路径并设置初始温度𝑻(𝟎) = 𝟏𝟎𝟎。

退火速率: 1. 𝑻(𝒏)=𝒍𝒓 ∗𝑻(𝒏−𝟏),我们设置的lr = 0.95。

2. 𝑻(𝒏)=𝑻(𝟎)/(𝐥𝐨𝐠⁡(𝟏+𝒏))

3.  𝑻(𝒏)=(𝑻(𝟎))/(𝟏+𝒏)

邻域搜索产生候选解:邻域函数(交换两个元素)。随机交换两个元素40次。

选择下一轮种子: 找到邻域中的最优值𝑋_𝑏𝑒𝑠𝑡,如果𝑿_𝒃𝒆𝒔𝒕优于𝑿_(𝒄𝒖𝒓_𝒃𝒆𝒔𝒕),则𝑿_𝒃𝒆𝒔𝒕作为下一轮种子。否则,下一轮种子 = 𝐞𝐱𝐩⁡((𝑿_𝒃𝒆𝒔𝒕−𝑿_(𝒄𝒖𝒓_𝒃𝒆𝒔𝒕))/𝑻(𝒌) ) > random[0,1)? 𝑿_𝒃𝒆𝒔𝒕: 𝑿_𝒏𝒐𝒘

以上述公式降低温度。若迭代次数达到100次,则停止。

(4)模拟退火算法算法结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.遗传算法

(1) 遗传算法基本思路
1. 根据问题的目标函数构造适值函数(即适应度)。 

2. 产生一个随即种群(100-1000)。

3. 根据适应度计算选择概率,不断进行繁殖。

4. 若干代后得到适应度最好的个体即最优解。
(2)遗传算法伪代码

在这里插入图片描述

(3)遗传算法具体实现细节

初始种群的产生: 随机产生一条路径,判断是否是可行解,若是则添加到初始种群中,若不是则舍弃。循环该过程直至初始种群数量达到指定值(100,500,1000)。

编码方法:用1~20组成的序列代表一条路径。

适应度:个体i的适应度 = 𝟏 − 𝒅_𝒊/(∑_(𝒋∈𝑵)▒𝒅_𝒋 ) 。其中𝑑_𝑖表示个体i的路径长度,N表示当前种群。

遗传运算:双切点交叉;变异概率2%

选择策略:个体i被选择概率 = 个体i的适应度/(种群数量-1),这一步是为了使所有个体被选择的概率和等于1。采用赌轮法随机选择父体。

停止准则:达到一定的迭代次数(迭代次数1000)。

(4)遗传算法结果

在这里插入图片描述

(5)图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.蚁群算法

(1) 蚁群算法基本思路
(2)蚁群算法伪代码
(3)蚁群算法具体实现细节
(4)蚁群算法算法结果

5.节约算法

  • 28
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Escape the bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值