装箱问题的算法研究
山东大学 赵一帆
问题描述
在箱子装载问题中,有若干个容量为c的箱子和n个待装载入箱子中的物品。物品i需占s[i]个单元(0<s[i]≤c)。所谓成功装载(feasible packing),是指能把所有物品都装入箱子而不溢出,而最优装载(optimal packing)则是指使用了最少箱子的成功装载。
问题分析
这道题目是算法课设发给我要求完成的题目,给了四种基本的算法,再加上avl树和竞赛树优化(竞赛树那部分我写的还是有点满意),后续我感觉可以用模拟退火来做一下,所以就搜集资料尝试了一下,之后百度百科说遗传算法也可以做,所以就自己想象凭空捏造了一波遗传算法,欢迎指出不足点。
其实这两种思路都是有更有目标和优化的搜索而已。
模拟退火思路
每次交换50个rand()对,如果替换结果就保留这个交换,否则不保留,不停在函数上面溜达,其实我也不知道这个函数长什么样子。
遗传算法思路
之前没有搞过这个算法,先学的,感觉很多二手博客非常不尊重达尔文同志的思想,强行忽略了遗传中很多的步骤,也有可能是他们生物没有学的很好,所以我加上了染色体交叉互换的环节,一共有n*(n/32)个染色体,每个染色体上面有32个基因,每个基因表示i和j是否进行交换,1表示为显性,0表示为隐形,这样空间最多可以表达<1000的数量,可惜最后进化效果并不是很好,大概进化的时间还是太短了,或者说这道题本身可能就不是很适合进化,因为箱子的交换有可能会走向好的方向,但是保留之后并不能显然的确定会向更好的方向进行移动。
性能分析
可以发现我写的退火算法和遗传算法确实不咋地,有时间再改进改进把,但是实际上我感觉真的挺难优化的。
大物品情况:
答案:
运行时间:
中等物品情况下:
答案:
运行时间:
小物品:
答案:
运行时间:
代码分析
void FF()
{
LL startTime = clock();
int hasNumber = 0;
for(int i = 1;i <= n;++i)
{
bool flag = false;
for(int j = 1;j <= hasNumber&&!flag;++j)
{
if(c_has[j] >= s[i])
{
c_has[j] -= s[i];
flag = true;
}
}
if(!flag) c_has[++hasNumber] = c - s[i];
}
cout<<"最先匹配法耗时"<<clock()-startTime<<"毫秒,";
cout<<"消耗箱子"<<hasNumber<<"个"<<endl;
}
int FFCT(int mode) //竞赛树做法
{
LL startTime = clock(),hasNumber = 0;
for(int i = 1;i <= 2*n;++i) cmt[i] = c;
for(int i = 1;i <= n;++i)
{
int x = 1;
while(x < n) {
if(cmt[x * 2] >= s[i]) x = x * 2;
else x = x * 2 + 1;
}
cmt[x] -= s[i];
while(x > 1) {
x = x/2;
cmt[x] = max(cmt[x*2],cmt[x*2 + 1]);
}
}
for(int i = n;i <= 2 * n - 1;++i)
if(cmt[i] != c) hasNumber++;
if(mode == 0)
{
cout<<"最先匹配(竞赛树优化)法耗时"<<clock()-startTime<<"毫秒,";
cout<<"消耗箱子"<<hasNumber<<"个"<<endl;
}
return hasNumber;
}
void FFD()
{
LL startTime = clock();
int hasNumber = 0;
for(int i = 1;i <= n;++i) s_max[i] = s[i];
sort(s_max+1,s_max+n+1,cmp);
for(int i = 1;i <= n;++i)
{
bool flag = false;
for(int j = 1;j <= hasNumber&&!flag;++j)
{
if(c_has[j] >= s_max[i])
{
c_has[j] -= s_max[i];
flag = true;
}
}
if(!flag) c_has[++hasNumber] = c - s_max[i]