一维装箱问题的多种解法(含源码)

源码网址
普通算法(ascend算法一般不常用,原因见下写在前面)

  • First-fit、First-fit-descend、First-fit-ascend
  • Best-fit、Best-fit-descend、Best-fit-ascend
  • Worst-fit、Worst -fit-descend、Worst -fit-ascend
  • Next-fit、Next -fit-descend、Next -fit-ascend
  • DP

元启发式算法

  • Simulated Annealing(SA 模拟退火)
  • Tabu Search(TS 禁忌搜索)
  • Genetic Algorithm(GA 遗传算法)

写在前面
descend/ascend就是对应先把物品进行降序/升序的排序再进行装箱,下文不再赘述。

其中ascend有个优点是下一个物品不需要遍历每个箱子,只需要检查存放上一个物品的箱子是否有足够的空间(之前的箱子连上一个较小的物品都放不下,所以不用考虑)即可,这样执行效率会比较高,但是结果出来发现需要的箱子较多,经过测试思考后发现descend算法是后面的物品有更高的概率把之前的箱子填满(也不一定要填满,就是更可能放入之前的箱子),空间利用率较高,而FFA算法后来的箱子一般都放不进原来的箱子,所以导致空间利用率低。比较适合箱子容量较大,而物品普遍较小的情况。

对上述解释没有概念的同学可以试试15 14 9 9 8 8 7 7 6 6 55 6 6 7 7 8 8 9 9 14 15两种顺序的装箱(箱子容量为20)

普通算法

  1. First-fit、First-fit-descend、First-fit-ascend
    对每个物品遍历当前箱子,把物品放入第一个能放下该物品的箱子,都放不下则新开一个箱子。

  2. Best-fit、Best-fit-descend、Best-fit-ascend
    对每个物品遍历当前箱子,把物品放入最合适放下该物品的箱子(即min(箱子容量-物品体积)),都放不下则新开一个箱子。

  3. Worst-fit、Worst -fit-descend、Worst -fit-ascend
    对每个物品遍历当前箱子,把物品放入最不合适放下该物品的箱子(即max(箱子容量-物品体积)),都放不下则新开一个箱子。

  4. Next-fit、Next -fit-descend、Next -fit-ascend
    查资料时发现的一个算法,原理为当处理一个物品的时候,检查当前箱子容量是否足够;如果足够,就将物品放入当前箱子,如果不足,就重新开辟一个新的箱子。

  5. DP
    把装箱问题看作背包问题,其中cost和value都是每个物品的体积(或质量),对每个背包尽可能地装满,然后记录装过地物品并去掉,直到所有物品装完。

元启发式算法

  1. Simulated Annealing
    模拟退火是一种Greedy算法,但是它的搜索过程引入了随机因素rand(注意加入时间种子保证随机性)。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出局部的最优解,达到全局的最优解。

  2. Tabu Search
    为了找到全局最优解,禁忌搜索标记已经解得的局部最优解或求解过程,并在进一步的迭代中避开这些局部最优解或求解过程,从而或得更多的搜索区域。

  3. Genetic Algorithm
    遗传算法是一种高效、并行、全局搜索的方法,能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地控制搜索过程以求得最佳解。

结果及分析

算法 时间复杂度 分析
First-fit、First-fit-descend、First-fit-ascend O(nlogn) 比较符合人的思维习惯,效果也较为良好,适合处理一般的中小型数据集
Best-fit、Best-fit-descend、Best-fit-ascend O(nlogn) 对比BF和WF,其实BF和WF都是FF的变种,没有太大的优劣之分,BF就是把剩余空间从小到大排序,WF就是把剩余空间从大到小排序。
Worst-fit、Worst-fit-descend、Worst-fit-ascend O(nlogn) 总体而言,BF较适合大物品比较多的任务(因为小任务多时经常不能填满),而WF适合小物品比较多的任务
Next-fit/Next-fit-descend、Next-fit-ascend O(n)/O(nlogn) 最直接的方法,虽然效率较高但是效果较差,像上述算法一样随便做点优化都能在稍微牺牲效率的情况下获得较好的结果提升,所以一般不常用。
DP O(nlogn) 纯属一个突然的思维拓展,想到用01背包的处理方法将一个背包尽可能地装满后再装下一个,其实上述算法除了NF之外都是这样的思想,不过是换了一种方法实现,虽然结果尚可,但是效率太低,不适用于解决装箱问题(应该有更好的实现暂时还没有想到)
算法 分析
Simulated-Annealing 自定义参数较多,对参数控制不熟悉且程序引入随机数,本身具有一定随机性,多次调整参数并尝试后才得到跳出局部最优的一个优于普通算法的result,由于需要自控制迭代次数,迭代次数一般都设置较大,所以时间效率方面一般
Tabu Search 也有些自定义参数和随机性,不知道是不是参数设置的原因,比起模拟退火更容易跳出局部最优得到优于一般算法的解,时间效率方面也优于模拟退火
Genetic-Algorithm 是本实验中结果表现最优的算法,由于是基于种群的启发式算法,所以遗传和选择时处理的数据量较大导致时间效率一般,但是由于其丰富的遗传规则和较大的测试数量所以更容易跳出局部最优

五. 实验总结与心得

  1. best-fit不是最优解,一维装箱不具有”最优子结构”,局部最优之和不等于全局最优;worst-fit也不是最差解,反而在小物品较多的装箱问题中有着更好地表现,以前是被名字误导,没有窥探其中的本质

  2. 升序排序是自己突然想到的,一考虑时间效率还以为是什么灵感大爆发,真正结果出来才知道原来降序是为了在填充过程中尽可能地塞满每个箱子,而升序则没有这种原理地体现,还是自己欠考虑了,包括DP也是,果然对于这种研究了几十上百年的问题老前辈们把能想的基本都想到了,难怪都看不到升序算法,再一次体会到了求学之路上的”任重而道远”

  3. 对于元启发式算法,都是具有一定的随机性的筛选,具体计算还是要结合普通算法。对于模拟退火和禁忌搜索等有较多自定义参数的要多尝试才能得出较好结果。在查找资料时发现一个很形象的总结摘录如下:

为了找出地球上最高的山,一群有志气的兔子们开始想办法:

  • 兔子朝着比现在高的地方跳去。他们找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是爬山法,它不能保证局部最优值就是全局最优值。
  • 兔子喝醉了。他随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,他渐渐清醒了并朝他踏过的最高方向跳去。这就是模拟退火
  • 兔子们知道一个兔的力量是渺小的。他们互相转告着,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。他们制定了下一步去哪里寻找的策略。这就是禁忌搜索
  • 兔子们吃了失忆药片,并被发射到太空,然后随机落到了地球上的某些地方。他们不知道自己的使命是什么。但是,如果你过几年就杀死一部分海拔低的兔子,多产的兔子们自己就会找到珠穆朗玛峰。这就是遗传算法

你可能感兴趣的:(算法)