CVRP问题中的Interchange算法,C++简单实现,可视化


注:本篇并不是最优解,只是将Interchange算法以一种直观的形式表现出来。是相对最优,运用了C++面向对象和EasyX图形库。

CVRP问题

有能力约束的车辆路径调度(Capacitated Vehicle Routing Problem,CVRP)
有能力约束的车辆路径问题,简称车辆路径问题。此模型是车辆路径问题的基本模型。该模型约束少,一般仅对车辆的载重和行驶的时间(或距离)有约束。此模型研究的时间最长,取得的成果最多,大量的精确算法,启发式算法用于求解此问题,其他模型的各种求解算法也大多衍生于此。

这里我们仅约束车辆的容量,不作其他约束。

Intercahnge算法

在这里插入图片描述
我们的输入如下:
在这里插入图片描述
num——结点的数量
capacity——一辆车的容量
mid——仓库的标号
下面依次是——标号,x坐标,y坐标,需求

思路

我们可以把整个问题分成两部分,“初始解”+“换路”。对于初始解,我采用的是爬山法,也就是每辆车从给定的仓库出发,向距离当前点最近的目标点前进,如果“目标点的需求”+“当前总需求”>“车辆的容量”,则将下一个结点置为仓库,也就是没法继续前进,只能回来。

这样走一遍爬山法,就可以得到一个初始解。接下俩只需要对其进行换路就可以了。

换路的路径和方式(如:1-0,1-1,1-2,2-1,2-0)为用户选择,那么从被换路的结点中按需求从小到大排列,选择要更换的结点。因为我们的约束条件是车的容量,可以导致该路径失败的因素也就只有需求。若最低的需求都无法更换,那这条路也就无法更换了。
当确定该结点可以更换后,就是选择插入的路径(注:此处的路径不是总路径的意思,比如总路径是1-2-3-4-5-1,此处的意思是1-2,或 2-3,或4-5这种两点间的路径)。对每两点间的路径进行计算,判断添加后增加的路径长度,选择最短的一条路径进行插入,这样就可以保证该条回路局部最优。

对于具体的增加结点,删除结点,判断操作,不作过多陈述,可查看下方代码。

#include "Mountain_Solution.h"
#include<iostream>
using namespace std;
Mountain_Solution::Mountain_Solution(graph g, int capacity)
{
	this->g = g;
	this->capacity = capacity;
	this->mid_house = g.get_nodelists()[g.get_MID()];
}
Mountain_Solution::Mountain_Solution()
{
	this->capacity = -1;
}
void Mountain_Solution::Mountain_Start()
{
	path p;
	g.get_nodelists()[g.get_MID()].set_visited(true);  //设置仓库结点为已访问状态
	while(!all_node_visited())	//所有结点未被全部访问
	{
		vector<path> temp;
		node start = mid_house;	//起始结点为仓库
		node next = g.getCLoseNext(mid_house.get_id());//下个结点是距离仓库最近的点
		int all_demand = start.get_demand() + next.get_demand();//计算加上下个结点的总需求
		while (all_demand <= capacity)//需求小于容量,可以到达下个结点
		{
			p.set_souce(start);
			p.set_destiny(next);
			temp.push_back(p);
			//设置路径并存入temp
			g.get_nodelists()[next.get_id()].set_visited(true);//设置目标结点为已访问
			all_demand -= next.get_demand();//减去目标结点,因为下一条路径目标节点就是起点,会加入计算
			start = next;
			next = g.getCLoseNext(start.get_id());
			all_demand += start.get_demand() + next.get_demand();
		}
		if (start.get_id() == mid_house.get_id())//如果开始结点等于仓库,说明当前车容量无法配送,容量不合适
		{
			cout << "The capacity is not appropriate" << endl;
			break;
		}
		//目标结点超过容量时,到达不了下个结点,直接返回
		p.set_souce(start);
		p.set_destiny(mid_house);
		temp.push_back(p);
		pa.push_back(temp);
	}
	calculatePathWeight();//计算总权值
}
bool Mountain_Solution::all_node_visited()//判断是否结点全被访问
{
	node* temp = g.get_nodelists();
	for (int i = 0; i < g.get_nodenum(); i++)
	{
		if (!temp[i].get_visited())
			return false;
	}
	return true;
}
void Mountain_Solution::calculatePathWeight()//计算一整条路径的权值
{
	vector<vector<path> >::iterator out_it;
	for (out_it = pa.begin(); out_it < pa.end(); out_it++)
	{
		vector<path>::iterator in_it;
		double temp = 0;
		for (in_it = (*out_it).begin(); in_it < (*out_it).end(); in_it++)
		{
			temp += (*in_it).calculate_weight();
		}
		total_weight.push_back(temp);
	}
}
void Mountain_Solution::show_result()
{
	int i = 1;
	vector<vector<path> > ::iterator out_it;
	vector<double> ::iterator dw_it = total_weight.begin();
	cout << "------------------------------------------------------" << endl;
	for (out_it = pa.begin(); out_it < pa.end(); out_it++)
	{
		cout << "The Road "<<i<<" is" << endl;
		vector<path>::iterator in_it;
		for (in_it = (*out_it).begin(); in_it < (*out_it).end(); in_it++)
		{
			cout << "(" << (*in_it).get_souce().get_id() << "," << (*in_it).get_destiny().get_id() << ")    ";
		}
		cout <<"The Weight:"<< (*dw_it) << endl;
		dw_it++;
		i++;
	}
	cout << "--------------------------------------------------------" << endl;

	cout << "Total Weight :" << calculateTotalWeight() << endl;
	cout << "------------------------------------------------------" << endl;
}

double Mountain_Solution::calculateTotalWeight()
{
	double total = 0;
	vector<double>::iterator it = total_weight.begin();
	for (; it < total_weight.end(); it++)
	{
		total += (*it);
	}
	return total;
}

void Mountain_Solution::set_pa(vector<vector<path> > pa)
{
	this->pa = pa;
}
int Mountain_Solution::get_capacity()
{
	return this->capacity;
}
vector<vector<path> > Mountain_Solution::get_pa()
{
	return this->pa;
}
vector<double> Mountain_Solution::get_totalweight()
{
	return this->total_weight;
}```

```cpp

#include "Interchange.h"
#include<iostream>
#include"graphic.h"
#include<conio.h>
#include <algorithm>
using namespace std;
Interchange::Interchange()
{
	this->capacity = -1;
	this->p1_id = -1;
	this->p2_id = -1;
}
Interchange::Interchange(graph g, vector<vector<path> > pa, int capacity)
{
	this->g = g;
	this->new_pa= pa;
	this->capacity = capacity;
	this->p1_id = -1;
	this->p2_id = -1;
}
vector<node> Interchange::Path_To_Node(vector<path> p)//路径转化成结点,即(1,2)(2,3)(3,4)(4,1)->{2,3,4}。初始节点是仓库,不需要
{
	vector<path>::iterator it;
	vector<node> p_nodelists;
	for (it=p.begin();it<p.end()-1;it++)
	{
		p_nodelists.push_back((*it).get_destiny());
	}
	return p_nodelists;
}
vector<node> Interchange::choose_LowestDemand_node(vector<path> p,int num)//选择最小的需求点,
{
	vector<node> res_node;
	vector<node>::iterator it, it_test;
	vector<node> p_nodelists = Path_To_Node(p);
	if (p_nodelists.size()<= (unsigned int)num)//路径结点小于要取出的结点,失败
	{
		cout << "The Road Customers Is Not Enough" << endl;
	}
	else
	{
		int i = 0;
		while (i < num)
		{
			int temp = DEMAND_MAX;
			for (it = p_nodelists.begin(); it < p_nodelists.end(); it++)
			{
				if ((*it).get_demand() < temp)
				{
					temp = (*it).get_demand();
					res_node.push_back(*it);
					it_test = it;
					break;
				}
			}
			p_nodelists.erase(it_test);
			i++;
		}
	}
	return res_node;
}
bool Interchange::isInsertable(vector<node> insert, vector<path> p, vector<node> except)//判断是否可以交换,路径的总需求 - 删除结点的需求 + 添加节点的需求 < 车容量
{
	int total_weight = 0;
	vector<path>::iterator path_it;
	vector<node> p_nodelists = Path_To_Node(p);
	vector<node>::iterator node_it;
	for (node_it = p_nodelists.begin(); node_it < p_nodelists.end(); node_it++)
	{
		total_weight = (*node_it).get_demand();
	}
	for (node_it = insert.begin(); node_it < insert.end(); node_it++)
	{
		total_weight += (*node_it).get_demand();
	}
	for (node_it = except.begin(); node_it < except.end(); node_it++)
	{
		total_weight -= (*node_it).get_demand();
	}
	if (total_weight > this->capacity)
		return false;
	else
		return true;
}
path Interchange::choose_MostSave_path(node insert, vector<path> p)//选择插入时增加权值最小的路径
{
	path res_path;
	double save = WEIGHT_MAX;
	vector<path>::iterator it;
	path test1, test2;
	for (it = p.begin(); it < p.end(); it++)
	{
		test1.set_souce((*it).get_souce());
		test1.set_destiny(insert);
		test2.set_souce(insert);
		test2.set_destiny((*it).get_destiny());

		double temp = test1.calculate_weight() + test2.calculate_weight() - (*it).calculate_weight();
		if ( temp < save)
		{
			save = temp;
			res_path = (*it);
		}
	}
	return res_path;
}
double Interchange::delete_node(vector<node> x, vector<path>& p)
{
	vector<path>::iterator p_it;
	path temp;
	double delete_weight = 0;
	vector<node>::iterator x_it;
	//删除结点
	for (x_it = x.begin(); x_it < x.end(); x_it++)
	{
		for (p_it = p.begin(); p_it < p.end(); p_it++)
		{
			if ((*p_it).get_destiny().get_id() == (*x_it).get_id())
			{
				temp.set_souce((*p_it).get_souce());
				delete_weight += (*p_it).get_weight();
				p_it = p.erase(p_it);

				temp.set_destiny((*p_it).get_destiny());
				delete_weight += (*p_it).get_weight();
				p_it = p.erase(p_it);
				
				p.insert(p_it, temp);
				temp.calculate_weight();
				delete_weight -= temp.get_weight();
				break;
			}
		}
	}
	return delete_weight;
}

double Interchange::insert_node(vector<node> x, vector<path>& p)//插入结点,返回增加的路径长度
{
	vector<node>::iterator x_it;
	vector<path>::iterator p_it;
	double add_weight = 0;
	for (x_it = x.begin(); x_it < x.end(); x_it++)
	{
		//得到要被插入的路径
		path beinserted = choose_MostSave_path(*x_it, p);
		path temp1, temp2;
		//定义两个插入后的路
		temp1.set_souce(beinserted.get_souce());
		temp1.set_destiny(*x_it);
		temp2.set_souce(*x_it);
		temp2.set_destiny(beinserted.get_destiny());
		temp1.calculate_weight();
		temp2.calculate_weight();
		add_weight += temp1.get_weight();
		add_weight += temp2.get_weight();

		for (p_it = p.begin(); p_it < p.end(); p_it++)
		{
			if ((*p_it).get_souce().get_id() == beinserted.get_souce().get_id())
			{
				//找到路径,删除,并添加新路径
				p_it=p.erase(p_it);
				p_it = p.insert(p_it, temp1);
				p.insert(++p_it, temp2);
				break;
			}
		}
	}
	return add_weight;
}

bool Interchange::Interchange_Start(int p1_id,int p2_id,int num1,int num2)
{
	this->p1_id = p1_id;
	this->p2_id = p2_id;
	vector<path> p1 = new_pa[p1_id - 1];
	vector<path> p2 = new_pa[p2_id - 1];
	vector<node> p1_choose = choose_LowestDemand_node(p1, num1);
	vector<node> p2_choose = choose_LowestDemand_node(p2, num2);

	TEST//
	if (p1_choose.size() != num1 || p2_choose.size() != num2)
		return false;
	else if (isInsertable(p1_choose, p2, p2_choose) && isInsertable(p2_choose, p1, p1_choose))
	{
		/强调线路和结点/
		graphic gic;
		gic.set_path(p1, PS_SOLID,GREEN);
		gic.set_path(p2, PS_SOLID,BROWN);

		vector<node>::iterator it;
		for (it = p1_choose.begin(); it < p1_choose.end(); it++)
		{
			gic.bold(*it,GREEN);
		}
		for (it = p2_choose.begin(); it < p2_choose.end(); it++)
		{
			gic.bold(*it,BROWN);
		}
		cout << "Press any key to continue...";
		char a = _getch();
		//
		
		double delete_weight = 0, add_weight = 0;
		new_p1 = p1;
		new_p2 = p2;
		delete_weight += delete_node(p1_choose, new_p1);
		add_weight += insert_node(p2_choose, new_p1);
		delete_weight += delete_node(p2_choose, new_p2);
		add_weight += insert_node(p1_choose, new_p2);

		///此段注释掉了,是判断添加后是否比原先的总代价低。但爬山法的路径已是较优,在调试中很难找到调换后总代价减少的点。

		/*if (delete_weight < add_weight)
		{
			cout << "This change will add the total weight!";
			return false;
		}
		else
		{*/
			vector<vector<path> > ::iterator new_it = new_pa.begin();
			for (int i = 0; i < p1_id - 1; i++)
			{
				new_it++;
			}
			//把调换前的路径删除,添加新的路径,形成新解new_pa
			new_it = new_pa.erase(new_it);
			new_pa.insert(new_it, new_p1);

			new_it = new_pa.begin();
			for (int i = 0; i < p2_id - 1; i++)
			{
				new_it++;
			}
			new_it = new_pa.erase(new_it);
			new_pa.insert(new_it, new_p2);

			calculatePathWeight();
			return true;
		//}
	}
	else
	{
		cout << "Please reduce the number of points. If it doesn't work,maybe one of the road can't be inserted point." << endl;
		return false;
	}
}
void Interchange::calculatePathWeight()
{
	vector<vector<path> >::iterator out_it;
	for (out_it = new_pa.begin(); out_it < new_pa.end(); out_it++)
	{
		vector<path>::iterator in_it;
		double temp = 0;
		for (in_it = (*out_it).begin(); in_it < (*out_it).end(); in_it++)
		{
			temp += (*in_it).calculate_weight();
		}
		new_total_weight.push_back(temp);
	}
}
void Interchange::show_result()
{
	int i = 1;
	vector<vector<path> > ::iterator out_it;
	vector<double> ::iterator dw_it = new_total_weight.begin();
	cout << "------------------------------------------------------" << endl;
	for (out_it = new_pa.begin(); out_it < new_pa.end(); out_it++)
	{
		cout << "The Road " << i << " is" << endl;
		vector<path>::iterator in_it;
		for (in_it = (*out_it).begin(); in_it < (*out_it).end(); in_it++)
		{
			cout << "(" << (*in_it).get_souce().get_id() << "," << (*in_it).get_destiny().get_id() << ")    ";
		}
		cout << "The Weight:" << (*dw_it) << endl;
		dw_it++;
		i++;
	}
	cout << "--------------------------------------------------------" << endl;

	vector<path>::iterator it;
	cout << "------------------------------------------------------" << endl;
	cout << "The New Road " << p1_id << " is" << endl;
	for (it = new_p1.begin(); it < new_p1.end(); it++)
	{
		cout << "(" << (*it).get_souce().get_id() << "," << (*it).get_destiny().get_id() << ")    ";
	}
	cout << "The Weight:" << new_total_weight[p1_id - 1] << endl;
	cout << "The New Road " << p2_id << " is" << endl;
	for (it = new_p2.begin(); it < new_p2.end(); it++)
	{
		cout << "(" << (*it).get_souce().get_id() << "," << (*it).get_destiny().get_id() << ")    ";
	}
	cout << "The Weight:" << new_total_weight[p2_id - 1] << endl;
	cout << "------------------------------------------------------" << endl;

	cout << "Total Weight :" << calculateTotalWeight() << endl;
	cout << "------------------------------------------------------" << endl;
}
double Interchange::calculateTotalWeight()
{
	double total = 0;
	vector<double>::iterator it = new_total_weight.begin();
	for (; it < new_total_weight.end(); it++)
	{
		total += (*it);
	}
	return total;
}
vector<path> Interchange::get_new_p1()
{
	return this->new_p1;
}
vector<path> Interchange::get_new_p2()
{
	return this->new_p2;
}
vector<vector<path> > Interchange::get_new_pa()
{
	return this->new_pa;
}

结果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
详情代码可查看我上传的资源——CVRP问题的Interchange算法,基本实现,直观可视化展示

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值