把圆形放入矩形的Packing问题

Packing问题,是一类组合优化问题,研究的是把一组较小的图形相互间无嵌入的放入较大的图形,其最终目标是寻求最优(放入最多的小图形)的放置方式。有效地求解此类问题,可以较好地利用资源、减少浪费,这对实际的生产领域,如物体的运输、堆放以及原料的下料等领域,具有不可估量的经济效益。

      早在10世纪初期,就有数学家们对此类问题进行了探索。但是由于Packing问题通常都是NP难度的,所以当问题规模增大到一定的程度时,目前尚未有任何算法可以保证能够在有效地时间内求解出最优解。但随着实际生产的迫切需求,研究者们提出了许多有效地搜索策略,采用启发式算法对此类问题进行了快速的近似求解。

      本文所涉及的仅仅是放入大矩形的圆形Packing问题,即求解把一组较小的圆形放入较大的矩形的最优解问题。采用的算法,主要参考了国内学者黄文奇教授等提出的拟人拟物策略,参考的文献链接在本文的末尾标注,感兴趣的同学可以深入研读。可以用拟物策略、最都下降算法、拟人策略来概括。我大致简略的进行了拜读,感觉黄文奇教授提出的这个拟人拟物思想挺有意思,对我们的学习工作也很有启发意义,所以建议有时间感兴趣的同学真的可以读一下,这里只做大体的介绍。

 

算法思想:      

      首先,给出k个小圆,尝试将它们放入大的矩形当中。若成功,则继续尝试将k+1个小圆放入其中;否则,算法结束,k就是最优解。下面详细叙述怎样进行有效地尝试以期能够把这k个小圆放入到大矩形当中。

      设矩形的左下角为坐标系的原点(0,0),假若能够把这k个小圆放入其中,则应满足如下条件:

 

 

其中R为小圆的半径长(这里为了简单,取了相同的小圆,如不同,只需半径变化,道理相同),xi、yi分别是第i个小圆的横纵坐标,L为大矩形的长,W为大矩形的宽。(下同)

        为寻找满足条件的解,先随机的把k个圆的圆心放入到大矩形当中,然后定义与圆形间以及圆形与边界间的嵌入深度相关的“势能”。

第i个与第j个圆之间的势能:


第i个圆与矩形的四条边之间的势能:



最后,定义整个系统的势能值:



        很容易理解,寻找放置k个圆的过程,即是寻找U(X)为0的过程。为了快速寻找到满足条件的解,这里选用最陡下降法进行下一轮圆心X,Y的选择。


算法描述:



最后,根据上面的介绍,给出具体的C++代码实现:

#include
#include
#include
#include
#include
using namespace std;

ifstream in;
ofstream out;

#define TURN 1000000000
#define PI 3.1415926
#define r 0.8
#define e1 0.000001
#define e2 0.000001

double WW[100];
double LL[100];
int LEN;
double W;
double L;
double R=sqrt(1/PI);

double x[100];
double y[100];
double u[100];
double U;
int m;

int t=0,k0=0;
double h=1;
void newposk(int);
void position()
{
	
	for(int i=0;iW)
		{
			u[i]+=y[i]+R-W;
		}
		if((x[i]+R)>L)
		{
			u[i]+=x[i]+R-L;
		}
		U+=u[i];
	}
	return U;
}
void newpos()
{
	double xx[100],yy[100];
	
	for(int i=0;iW)
		{
			yy[i]-=h*(y[i]+R-W);
		}
		if((x[i]+R)>L)
		{
			xx[i]-=h*(x[i]+R-L);
		}
		
	}
	
	for(int i=0;iu[i])
		{
			energy=i;
		}
	}
	return energy;
}
void newposk(int k)
{
	x[k]=(W*rand()/(RAND_MAX+1.0));
	y[k]=(L*rand()/(RAND_MAX+1.0));
}
int step()
{
	int turn=0;
	while(turn=x0)
			{
				h=r*h;
			}
			x0=x1;
			if(h>=e1)
			{
				flag=true;
				int k=maxenergy();
				if(k==k0)
				{
					t=t+1;
				}
				if(t<1)
				{
					newposk(k);
					h=1;
					k0=k;
				}
				else if(t==1)
				{
					k=minenergy();
					newposk(k);
					h=1;
					k0=0;
					t=0;
				}
			}
			turn++;
		}

	}
	return 0;
}
int main()
{
	srand((unsigned)time(0));
	in.open("LW.txt",ios::in);
	in>>LEN;
	for(int i=0;i>LL[i]>>WW[i];
	}
	in.close();
	out.open("cir.txt",ios::out);
	out<<"L\tW\ttotal\t"<

         不过,遗憾的是,虽然几经修改,但是这个代码的实现效果距离最优解仍然具有差距...所以在这里也请求大牛们如果发现代码实现过程中有什么问题或者可以改进的方向,欢迎批评指正,会非常感谢您的不吝赐教!!!


参考资料链接:

http://www.cnki.net/KCMS/detail/detail.aspx?QueryID=26&CurRec=1&recid=&filename=2003112288.nh&dbname=CDFD2004&dbcode=CDFD&pr=&urlid=&yx=

http://www.cnki.net/KCMS/detail/detail.aspx?QueryID=6&CurRec=1&recid=&filename=JSGG200335009&dbname=CJFD2003&dbcode=CJFQ&pr=&urlid=&yx=

http://www.cnki.net/KCMS/detail/detail.aspx?QueryID=10&CurRec=8&recid=&filename=JFYZ200204004&dbname=cjfd2002&dbcode=CJFQ&pr=&urlid=&yx=

不方便中国知网下载的同学,可以参照下面链接下载:

http://download.csdn.net/detail/smarteryu/5097679

http://download.csdn.net/detail/smarteryu/5097680


你可能感兴趣的:(乱七八糟)