车辆配送路径选择问题分析
配送問題
- 目錄
- 問題及數據
- 1. 問題說明
- 2. 測試數據和說明
- 3. 數據
- 思路一:貪心算法
- 1. 原始方案
- [1] 過程分析
- [2] 例子分析
- [3] 結果分析
- [4] 案例代碼
- 2. 改進嘗試A-失敗(╯▔皿▔)╯
- a. 改進點討論
- b.例子分析
- c. 結果分析
- c. 代碼
- 3. 改進嘗試B-略有改進(注意:理論上貌似行不通)
- a. 改進點討論
- b.例子分析
- c. 結果分析
- c. 代碼
- 4. 改進嘗試C-在改進A中引入B的比值法
- a. 改進點討論
- b. 結果分析
- 思路二:暴力求解
- 1.思路分析
- 2.過程分析
- 3.判斷條件
- 4.結果分析
- 5.代碼
- 思路三:智能算法
- 代碼
- 1.禁忌算法
- (1) 禁忌算法基本思路
- (2)禁忌算法偽代碼
- (3)禁忌算法具體實現細節
- (4)禁忌算法算法結果
- 2.模擬退火算法
- (1) 模擬退火算法基本思路
- (2)模擬退火算法偽代碼
- (3)模擬退火算法具體實現細節
- (4)模擬退火算法算法結果
- 3.遺傳算法
- (1) 遺傳算法基本思路
- (2)遺傳算法偽代碼
- (3)遺傳算法具體實現細節
- (4)遺傳算法結果
- (5)圖
- 4.蟻群算法
- (1) 蟻群算法基本思路
- (2)蟻群算法偽代碼
- (3)蟻群算法具體實現細節
- (4)蟻群算法算法結果
- 5.節約算法
目錄
算法就是探索的過程嘛~,記錄問題研究過程,不專業,其中可能有些錯誤~還望指正!問題及數據
1. 問題說明
通過實際案例描述,根據配送點和業務需求量,進行最優路線的計算。由物流中心點出發,配送多個客戶點后再回到起點,根據車輛數量,承載限制,不同車輛服務成本、運行里程限制等條件選擇最優運輸路徑(總里程最短),使成本最小化,配送訂單最大化,滿載率最大化(如由一個配送中心向各個銷售點配送貨物,通過算法確定配送中心每輛車的配送方案,包括配送至哪個客戶,配送量,下一個配送目的地)。
2. 測試數據和說明
某物流中心有5臺配送車輛,車輛的最大載重均為8T,一次配送的最大行駛距離都為50KM,需要向20個客戶送貨,物流中心和20個客戶的坐標及其客戶的需求量隨機產生,其中,物流中心的坐標為(14.2KM,13.1km),要求合理安排車輛的配送路線和載重量,使配送總里程最短。
3. 數據
| 1 | 12.8 | 8.5 | 0.1 |
| 2 | 18.4 | 3.4 | 0.4 |
| 3 | 15.4 | 16.6 | 1.2 |
| 4 | 18.9 | 15.2 | 1.5 |
| 5 | 15.5 | 11.6 | 0.8 |
| 6 | 3.9 | 10.6 | 1.3 |
| 7 | 10.6 | 7.6 | 1.7 |
| 8 | 8.6 | 8.4 | 0.6 |
| 9 | 12.5 | 2.1 | 1.2 |
| 10 | 13.8 | 5.2 | 0.4 |
| 11 | 6.7 | 16.9 | 0.9 |
| 12 | 14.8 | 2.6 | 1.3 |
| 13 | 1.8 | 8.7 | 1.3 |
| 14 | 17.1 | 11 | 1.9 |
| 15 | 7.4 | 1 | 1.7 |
| 16 | 0.2 | 2.8 | 1.1 |
| 17 | 11.9 | 19.8 | 1.5 |
| 18 | 13.2 | 15.1 | 1.6 |
| 19 | 6.4 | 5.6 | 1.7 |
| 20 | 9.6 | 14.8 | 1.5 |
說明:各客戶相互之間和物流中心與客戶之間的距離均采用直線距離
思路一:貪心算法
1. 原始方案
[1] 過程分析
貪心算法其實很簡單。
即每次選擇距離最小的點,然后進行判斷:
如果都可以那么加入此點,尋找下一個距離最近的點,直到不滿足條件回到出發點。
第一輛車回到出發點后,如果還存在未運輸的城市,那么繼續采用第二輛車以此類推,直到城市全部運輸完成。
[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;//下一步設置為didata_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;//下一步設置為didata_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;//下一步設置為didata_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;//下一步設置為didata_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億次得出的最小的總路徑:
路徑圖:
- 2億次得出的最小的總路徑:
路徑圖:
5.代碼
思路三:智能算法
代碼
https://download.csdn.net/download/jnbfknasf113/18908669?spm=1001.2014.3001.5503
1.禁忌算法
(1) 禁忌算法基本思路
1.選定一個初始解𝑋_𝑛𝑜𝑤;令禁忌表𝐻=? 。
若滿足終止準則,則轉第四步;否則,在𝑋_𝑛𝑜𝑤的鄰域𝑁(𝑋_𝑛𝑜𝑤)中選出滿足禁忌要求的候選集𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤),轉第三步。
在𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤)中選一個評價值最好的解𝑋_𝑏𝑒𝑠𝑡,令𝑋_𝑛𝑜𝑤=𝑋_𝑏𝑒𝑠𝑡,更新禁忌表H,轉第二步。
輸出計算結果,停止。
(2)禁忌算法偽代碼
(3)禁忌算法具體實現細節
初始化:隨機產生一個可行路徑作為初始解,將其設置為最優路徑。清空禁忌表,設置禁忌長度(5, 10, 15)。
鄰域搜索產生候選解:鄰域函數(交換兩個元素)。我們探測該解的全鄰域,即一共190個解(19+18+ … +2+1),將其中滿足限制條件(重量、距離)且未被禁忌的解挑選出來作為鄰域。
選擇最好的候選解:在鄰域中選出適應度(距離最短)最好的候選解,并將其與當前最優值進行比較。如果優于當前最佳解,則更新最佳解。更新禁忌表(禁忌該操作方式,并將其他被禁方式禁忌長度減1),將該最好的候選解作為下一輪的種子。
判斷終止條件: 迭代是否達到200次。
(4)禁忌算法算法結果
2.模擬退火算法
(1) 模擬退火算法基本思路
1.選定一個初始解𝑋_𝑛𝑜𝑤;設定初始溫度𝑇_s。
若滿足終止準則,則轉第四步;否則,在𝑋_𝑛𝑜𝑤的鄰域𝑁(𝑋_𝑛𝑜𝑤 ) 中選出候選集𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤),轉第三步。
在𝐶𝑎𝑛_𝑁(𝑋_𝑛𝑜𝑤)中選一個評價值最好的解𝑋_𝑏𝑒𝑠𝑡。如果𝑋_best優于𝑋_(𝑐𝑢𝑟_𝑏𝑒𝑠𝑡),則令𝑋_𝑛𝑜𝑤=𝑋_𝑏𝑒𝑠𝑡,并且更新𝑋_(𝑐𝑢𝑟_𝑏𝑒𝑠𝑡);否則,以一定的概率p接受𝑋_𝑏𝑒𝑠𝑡 作為下一輪的種子。降溫,轉第二步。
輸出計算結果,停止。
(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.節約算法
總結
以上是生活随笔為你收集整理的车辆配送路径选择问题分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea热部署与重启
- 下一篇: 1.安装msys64_2、vs2017编