基于退火算法的启发式下料问题(C++)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31880107/article/details/51282476


刚刚做完最优控制的大作业,总结下所用的模拟退火算法,谈谈自己的理解。

1. 问题描述

问题就是典型的下料问题。把板材切割成要求数量和规格的产品。其中,输入为板材的大小、产品的数量和大小,输出为余料率、产品的坐标。

具体算法概述参见百度百科吧模拟退火

2. 自己的理解

退火过程中的每个金属分子有能量和状态两种属性,将能量最低时的状态记做r,能量和状态的概率有对应关系,能量越高,分子状态为r的概率越低,反之在最低温度时,r状态的概率趋近于1。将组合优化问题看做金属,解看做状态,那么最优解就是能量最低的状态,同时也定义一个费用函数来表示能量,通常都用T乘某个略小于1的系数。当T值比较高时,状态变化比较活跃,当T值达到最低时,达到最优解的概率趋近于1。

第一次接触模拟退火算法,表示这的确是一个不错的方法。对于求最优解的问题,的确能节约不少计算时间。模拟退火算法的精髓就在于其将收缩扩张动态结合在一起,得到最优解是收缩的过程,而为了避免陷入局部最优解,又得有扩张的表现。再次印证了我一直以来坚信的一个想法,大自然演化的很巧妙,如果有什么无法解决的问题,或者想有什么创新,就到大自然中去找吧,哈哈,说多了。下面具体谈谈模拟退火。

3. 算法详细讲解

3.1 算法步骤

  1. 任选一初始解 x 0 x_0 。 给定初始温度 T m a x T_{max} ;
  2. 若在该温度达到内循环停止条件,转第3步,否则,从解邻域 N ( x i ) N(x_i) 中随机选取 x j x_j ,计算目标函数的差值,如果小于0,则 x i = x j x_i=x_j ,否则,计算 e x p [ ( f ( X j ) f ( x i ) ) / T ] > r a n d o m ( 0 , 1 ) exp[(-f(Xj)-f(xi))/T]>random(0,1) x i = x j x_i=x_j ;重复2
  3. 更新温度 T = d ( T ) T=d(T) ,若满足外循环停止条件,中止计算,否则,返回2。

更详细的介绍请看我上传的我们老师讲课的PPT,里面证明了此算法的适用条件,并证明了此算法能够达到全局最优解。

3.2 问题需要满足的条件

要想应用此算法,问题必须满足以下条件:

  1. 可达性。即无论起点如何,任何一个状态都可以到达。这样才使我们有得到最优解的可能
  2. 渐进不依赖起点。由于起点的选择有非常大的随机性,我们的目的是达到全局最优,因此,应渐进的不依赖起点。
  3. 分布稳定性。包含两个内容:其一是当温度不变时,其马尔科夫链的极限分布存在;其二是当温度渐进0时,其马尔科夫链也有极限分布。(马尔科夫链就是一种随机状态序列,可以直观的理解为一个解)

3.3 实现算法需要确定几个量

然后需要确定以下几个量:

  1. 解的形式
  2. 邻域的选取
  3. 初始温度的选取
  4. 温度下降的规则
  5. 每一温度下马氏链的迭代步长和停止规则

只要确定了以上这些量的具体定义,就可以根据上面提到的步骤进行计算了。

3.4 问题分析

对于下料问题,

  1. 产品的下料顺序看成问题的一个解,启发式安排下料,得出余料率(余料率就是目标函数,其值越小越好,最优解即是余料率最小时的下料顺序)。余料率小于5%时,停止计算;
  2. 邻域的构造采用2-opt,即随机改变其中两个产品的生产顺序。因为是随机改变两个顺序,所以邻域中每个解的选取概率都是相同的
  3. 起始温度可以选择T=100,温度下降就用T=T*k,k可以去略小于1的数,比如0.96,终止温度可选10;
  4. 迭代步长就选用最简单的非时齐,每一温度下只迭代一步。

3.5 计算余料率的方法

最后,对于每个解计算余料率时用的启发式方法(对于每个要生产的产品,优先占角)(此部分算法是参考别的论文写的)如下:

  1. 找到当前板材中的可用角,(只考虑左下角,即└形状的角),如果没有就取一张新的板材,
  2. 计算下一个要切割的产品占哪个角比较好,规则如下:
    (1)计算到产品到形成该角以外的其他矩形块(包括板材边)的最小距离Dmin,此值越小越好。
    (2)如果有n个角的Dmin值相同,则计算此角的贴边率(边重叠总长/产品周长)
    (3)如果还是有相同的角,就按序号优先级了
  3. 直到所有的产品都切割完,输出余料率。

4. C++代码部分

//主程序部分
for (; T > 10; T*=0.96)
	{
		tuihuo(productbuf, material, alproduct, T, ExceStock);//迭代一步
		cout << ExceStock << " " << endl;
		if (ExceStock <= 0.01)break;
	}
//退火算法部分
void tuihuo(deque<stock> &probuf, const stock &matrl, vector<stock> &alpro, double &T, double &excestock)
{
	deque<stock> probuf1(probuf);//放置交换后的一个邻域
	vector<stock> alpro1;        //放置交换后的解启发式运算结果
	twoOpt(probuf, probuf1);
	double exstock, exstock1;//余料率
	alpro.clear();
	exstock = ProductLayout(probuf, matrl, alpro);
	exstock1 = ProductLayout(probuf1, matrl, alpro1);

	if (exstock1 <= exstock)//如果邻域更好
	{
		probuf.swap(probuf1);
		exstock = exstock1;
		alpro = alpro1;
	}
	else
	{
		double P = exp((exstock - exstock1)/T);//概率
		static default_random_engine e1;
		static uniform_int_distribution<unsigned> u1(0, 1);
		if (P > u1(e1))
		{
			probuf.swap(probuf1);
			exstock = exstock1;
			alpro = alpro1;
		}
	}
	excestock = exstock;
}

猜你喜欢

转载自blog.csdn.net/qq_31880107/article/details/51282476