当前位置: 首页 > news >正文

贪心算法应用:钢铁连铸优化问题详解

在这里插入图片描述

Java中的贪心算法应用:钢铁连铸优化问题详解

1. 问题背景与定义

钢铁连铸优化问题(Steel Cutting Problem)是工业生产中的一个经典优化问题。在钢铁生产过程中,我们需要将长钢坯切割成不同长度的钢条以满足客户订单需求,目标是最小化浪费或最大化利润。

问题形式化描述

给定:

  • 一根长度为L的原始钢坯
  • 一组客户订单需求,每个需求指定了长度lᵢ和数量nᵢ
  • 可能的切割模式集合

目标:
寻找一组切割方案,使得:

  1. 所有客户需求得到满足
  2. 使用的原始钢坯数量最少(或总浪费最少)

2. 贪心算法原理

贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。对于钢铁连铸问题,常见的贪心策略包括:

  1. 最大剩余优先(Largest Remaining First):每次选择能放入当前钢坯的最大可能需求
  2. 最小剩余优先(Smallest Remaining First):每次选择能放入当前钢坯的最小可能需求
  3. 价值密度优先:按单位长度的价值排序

3. 问题建模与Java实现

3.1 数据结构定义

首先定义基本的数据结构和类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;// 表示一个客户订单需求
class Order {int length;  // 需要的钢条长度int quantity; // 需要的数量public Order(int length, int quantity) {this.length = length;this.quantity = quantity;}
}// 表示一个切割方案
class CuttingPattern {List<Integer> cuts;  // 当前方案中的切割长度int remaining;       // 剩余长度public CuttingPattern(int stockLength) {this.cuts = new ArrayList<>();this.remaining = stockLength;}// 尝试添加一个切割public boolean addCut(int length) {if (length <= remaining) {cuts.add(length);remaining -= length;return true;}return false;}@Overridepublic String toString() {return "Pattern: " + cuts + ", Remaining: " + remaining;}
}// 表示完整的解决方案
class Solution {List<CuttingPattern> patterns;int totalWaste;public Solution() {patterns = new ArrayList<>();totalWaste = 0;}public void addPattern(CuttingPattern pattern) {patterns.add(pattern);totalWaste += pattern.remaining;}public void printSolution() {System.out.println("Total patterns used: " + patterns.size());System.out.println("Total waste: " + totalWaste);for (int i = 0; i < patterns.size(); i++) {System.out.println("Pattern " + (i+1) + ": " + patterns.get(i));}}
}

3.2 贪心算法实现:最大剩余优先

public class SteelCuttingGreedy {// 最大剩余优先算法public static Solution greedyLargestFirst(int stockLength, List<Order> orders) {// 复制订单以避免修改原始数据List<Order> remainingOrders = new ArrayList<>();for (Order order : orders) {remainingOrders.add(new Order(order.length, order.quantity));}// 按长度降序排序Collections.sort(remainingOrders, new Comparator<Order>() {@Overridepublic int compare(Order o1, Order o2) {return Integer.compare(o2.length, o1.length);}});Solution solution = new Solution();CuttingPattern currentPattern = new CuttingPattern(stockLength);while (!remainingOrders.isEmpty()) {boolean addedAny = false;// 尝试添加最大的可能的订单for (int i = 0; i < remainingOrders.size(); i++) {Order order = remainingOrders.get(i);if (order.length <= currentPattern.remaining && order.quantity > 0) {currentPattern.addCut(order.length);order.quantity--;addedAny = true;// 如果订单数量为0,从列表中移除if (order.quantity == 0) {remainingOrders.remove(i);i--; // 调整索引}// 重新排序,因为剩余空间变化了Collections.sort(remainingOrders, new Comparator<Order>() {@Overridepublic int compare(Order o1, Order o2) {return Integer.compare(o2.length, o1.length);}});break; // 每次只添加一个,然后重新评估}}// 如果不能添加任何订单,完成当前patternif (!addedAny) {if (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}currentPattern = new CuttingPattern(stockLength);// 检查是否还有订单可以放入新的patternboolean canProceed = false;for (Order order : remainingOrders) {if (order.length <= stockLength && order.quantity > 0) {canProceed = true;break;}}if (!canProceed) {break;}}}// 添加最后一个patternif (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}return solution;}public static void main(String[] args) {int stockLength = 100; // 原始钢坯长度List<Order> orders = new ArrayList<>();orders.add(new Order(45, 3));orders.add(new Order(30, 5));orders.add(new Order(25, 4));orders.add(new Order(15, 7));System.out.println("Greedy Largest First Solution:");Solution solution = greedyLargestFirst(stockLength, orders);solution.printSolution();}
}

3.3 贪心算法实现:最小剩余优先

// 在SteelCuttingGreedy类中添加以下方法// 最小剩余优先算法
public static Solution greedySmallestFirst(int stockLength, List<Order> orders) {// 复制订单以避免修改原始数据List<Order> remainingOrders = new ArrayList<>();for (Order order : orders) {remainingOrders.add(new Order(order.length, order.quantity));}// 按长度升序排序Collections.sort(remainingOrders, new Comparator<Order>() {@Overridepublic int compare(Order o1, Order o2) {return Integer.compare(o1.length, o2.length);}});Solution solution = new Solution();CuttingPattern currentPattern = new CuttingPattern(stockLength);while (!remainingOrders.isEmpty()) {boolean addedAny = false;// 尝试添加最小的可能的订单for (int i = 0; i < remainingOrders.size(); i++) {Order order = remainingOrders.get(i);if (order.length <= currentPattern.remaining && order.quantity > 0) {currentPattern.addCut(order.length);order.quantity--;addedAny = true;// 如果订单数量为0,从列表中移除if (order.quantity == 0) {remainingOrders.remove(i);i--; // 调整索引}break; // 每次只添加一个,然后重新评估}}// 如果不能添加任何订单,完成当前patternif (!addedAny) {if (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}currentPattern = new CuttingPattern(stockLength);// 检查是否还有订单可以放入新的patternboolean canProceed = false;for (Order order : remainingOrders) {if (order.length <= stockLength && order.quantity > 0) {canProceed = true;break;}}if (!canProceed) {break;}}}// 添加最后一个patternif (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}return solution;
}// 在main方法中添加测试
System.out.println("\nGreedy Smallest First Solution:");
Solution smallFirstSolution = greedySmallestFirst(stockLength, orders);
smallFirstSolution.printSolution();

4. 算法分析与优化

4.1 时间复杂度分析

  • 排序阶段:O(n log n),其中n是订单数量
  • 分配阶段:最坏情况下O(n²),因为每次添加一个切割后可能需要重新扫描订单列表
  • 总体复杂度:O(n²)

4.2 空间复杂度分析

  • 需要O(n)的额外空间存储订单副本和解决方案

4.3 算法优化方向

  1. 更高效的数据结构:使用优先队列(PriorityQueue)来维护订单
  2. 回溯机制:当发现当前选择导致后续无法满足时,回退一步尝试其他选择
  3. 启发式规则组合:结合多种贪心策略,选择最优解

4.4 优化后的实现(使用PriorityQueue)

// 在SteelCuttingGreedy类中添加以下方法// 使用PriorityQueue的最大剩余优先
public static Solution greedyLargestFirstPQ(int stockLength, List<Order> orders) {// 创建最大堆PriorityQueue<Order> maxHeap = new PriorityQueue<>((o1, o2) -> Integer.compare(o2.length, o1.length));// 添加订单到堆中for (Order order : orders) {if (order.quantity > 0) {maxHeap.add(new Order(order.length, order.quantity));}}Solution solution = new Solution();CuttingPattern currentPattern = new CuttingPattern(stockLength);while (!maxHeap.isEmpty()) {Order order = maxHeap.peek();if (order.length <= currentPattern.remaining) {// 可以添加到当前patterncurrentPattern.addCut(order.length);order.quantity--;if (order.quantity == 0) {maxHeap.poll(); // 移除数量为0的订单}} else {// 不能添加,尝试下一个最大的Order nextOrder = null;boolean found = false;// 临时列表存储无法添加的订单List<Order> temp = new ArrayList<>();while (!maxHeap.isEmpty()) {nextOrder = maxHeap.poll();if (nextOrder.length <= currentPattern.remaining && nextOrder.quantity > 0) {found = true;break;}temp.add(nextOrder);}// 将临时列表中的订单重新放回堆中for (Order o : temp) {maxHeap.add(o);}if (found) {currentPattern.addCut(nextOrder.length);nextOrder.quantity--;if (nextOrder.quantity > 0) {maxHeap.add(nextOrder);}} else {// 没有可以添加的订单,完成当前patternif (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}currentPattern = new CuttingPattern(stockLength);// 检查是否还有订单可以放入新的patternif (maxHeap.isEmpty() || maxHeap.peek().length > stockLength) {break;}}}}// 添加最后一个patternif (!currentPattern.cuts.isEmpty()) {solution.addPattern(currentPattern);}return solution;
}// 在main方法中添加测试
System.out.println("\nGreedy Largest First with PriorityQueue Solution:");
Solution pqSolution = greedyLargestFirstPQ(stockLength, orders);
pqSolution.printSolution();

5. 测试与验证

5.1 测试用例设计

设计多种测试用例来验证算法的正确性和效率:

  1. 基本测试用例:少量订单,能完全利用钢坯
  2. 边界测试用例:订单长度等于钢坯长度
  3. 压力测试:大量订单,多种长度组合
  4. 无法满足测试:有订单长度超过钢坯长度

5.2 测试代码示例

public static void testCases() {// 测试用例1:基本测试System.out.println("\nTest Case 1: Basic Test");List<Order> test1 = new ArrayList<>();test1.add(new Order(50, 2));test1.add(new Order(30, 3));Solution sol1 = greedyLargestFirst(100, test1);sol1.printSolution();// 测试用例2:边界测试System.out.println("\nTest Case 2: Boundary Test");List<Order> test2 = new ArrayList<>();test2.add(new Order(100, 2));test2.add(new Order(50, 2));Solution sol2 = greedyLargestFirst(100, test2);sol2.printSolution();// 测试用例3:压力测试System.out.println("\nTest Case 3: Stress Test");List<Order> test3 = new ArrayList<>();Random rand = new Random();for (int i = 0; i < 20; i++) {test3.add(new Order(10 + rand.nextInt(40), 1 + rand.nextInt(5)));}Solution sol3 = greedyLargestFirst(100, test3);sol3.printSolution();// 测试用例4:无法满足测试System.out.println("\nTest Case 4: Impossible Test");List<Order> test4 = new ArrayList<>();test4.add(new Order(120, 1));try {Solution sol4 = greedyLargestFirst(100, test4);sol4.printSolution();} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());}
}// 在main方法中调用
testCases();

6. 贪心算法的局限性及改进

6.1 贪心算法的局限性

  1. 局部最优不等于全局最优:贪心算法可能陷入局部最优解
  2. 无法回溯:一旦做出选择就不能撤销
  3. 对问题结构敏感:某些问题结构可能导致贪心算法表现很差

6.2 替代方案

  1. 动态规划:对于小规模问题,可以使用动态规划获得精确解
  2. 分支限界法:在合理时间内找到较好的解
  3. 遗传算法:对于大规模问题,可以使用元启发式算法

6.3 混合方法示例

结合贪心和回溯的改进算法:

// 在SteelCuttingGreedy类中添加以下方法// 带有限回溯的贪心算法
public static Solution greedyWithBacktracking(int stockLength, List<Order> orders, int backtrackDepth) {// 复制订单List<Order> remainingOrders = new ArrayList<>();for (Order order : orders) {if (order.quantity > 0) {remainingOrders.add(new Order(order.length, order.quantity));}}// 按长度降序排序Collections.sort(remainingOrders, (o1, o2) -> Integer.compare(o2.length, o1.length));Solution bestSolution = new Solution();backtrack(stockLength, remainingOrders, new Solution(), new CuttingPattern(stockLength), backtrackDepth, bestSolution);return bestSolution;
}private static void backtrack(int stockLength, List<Order> remainingOrders, Solution currentSolution, CuttingPattern currentPattern, int depth, Solution bestSolution) {// 基准情况:所有订单都满足if (remainingOrders.isEmpty()) {if (!currentPattern.cuts.isEmpty()) {currentSolution.addPattern(currentPattern);}if (currentSolution.totalWaste < bestSolution.totalWaste || bestSolution.patterns.isEmpty()) {bestSolution.patterns = new ArrayList<>(currentSolution.patterns);bestSolution.totalWaste = currentSolution.totalWaste;}return;}// 如果达到回溯深度限制,使用贪心算法完成剩余部分if (depth <= 0) {Solution greedySolution = greedyLargestFirst(stockLength, remainingOrders);for (CuttingPattern p : greedySolution.patterns) {currentSolution.addPattern(p);}if (currentSolution.totalWaste < bestSolution.totalWaste || bestSolution.patterns.isEmpty()) {bestSolution.patterns = new ArrayList<>(currentSolution.patterns);bestSolution.totalWaste = currentSolution.totalWaste;}return;}// 尝试添加每个可能的订单for (int i = 0; i < remainingOrders.size(); i++) {Order order = remainingOrders.get(i);if (order.length <= currentPattern.remaining && order.quantity > 0) {// 创建副本以避免修改原始数据List<Order> newRemaining = new ArrayList<>();for (Order o : remainingOrders) {newRemaining.add(new Order(o.length, o.quantity));}// 应用选择CuttingPattern newPattern = new CuttingPattern(currentPattern);newPattern.addCut(order.length);newRemaining.get(i).quantity--;// 移除数量为0的订单if (newRemaining.get(i).quantity == 0) {newRemaining.remove(i);}// 继续递归if (newPattern.remaining == 0 || newRemaining.stream().noneMatch(o -> o.length <= newPattern.remaining && o.quantity > 0)) {Solution newSolution = new Solution();newSolution.patterns = new ArrayList<>(currentSolution.patterns);newSolution.totalWaste = currentSolution.totalWaste;newSolution.addPattern(newPattern);backtrack(stockLength, newRemaining, newSolution, new CuttingPattern(stockLength), depth - 1, bestSolution);} else {backtrack(stockLength, newRemaining, currentSolution, newPattern, depth - 1, bestSolution);}}}// 尝试不添加任何订单,完成当前patternif (!currentPattern.cuts.isEmpty()) {Solution newSolution = new Solution();newSolution.patterns = new ArrayList<>(currentSolution.patterns);newSolution.totalWaste = currentSolution.totalWaste;newSolution.addPattern(currentPattern);backtrack(stockLength, remainingOrders, newSolution, new CuttingPattern(stockLength), depth - 1, bestSolution);}
}// 在main方法中添加测试
System.out.println("\nGreedy with Backtracking Solution:");
Solution backtrackSolution = greedyWithBacktracking(100, orders, 2);
backtrackSolution.printSolution();

7. 实际应用中的考虑因素

在实际工业应用中,还需要考虑以下因素:

  1. 切割损耗:每次切割会有固定宽度的材料损耗
  2. 切割顺序:某些切割顺序可能更高效
  3. 设备限制:切割机器可能有最小/最大切割长度限制
  4. 多目标优化:同时考虑时间、成本、浪费等多个目标

7.1 考虑切割损耗的改进实现

// 修改CuttingPattern类
class CuttingPattern {List<Integer> cuts;int remaining;int cutsMade; // 切割次数int kerfWidth; // 每次切割的损耗public CuttingPattern(int stockLength, int kerfWidth) {this.cuts = new ArrayList<>();this.remaining = stockLength;this.cutsMade = 0;this.kerfWidth = kerfWidth;}// 尝试添加一个切割,考虑切割损耗public boolean addCut(int length) {int required = length + (cuts.isEmpty() ? 0 : kerfWidth);if (required <= remaining) {if (!cuts.isEmpty()) {cutsMade++;}cuts.add(length);remaining -= required;return true;}return false;}@Overridepublic String toString() {return "Pattern: " + cuts + ", Remaining: " + remaining + ", Cuts made: " + cutsMade;}
}// 修改Solution类
class Solution {List<CuttingPattern> patterns;int totalWaste;int totalCuts;public Solution() {patterns = new ArrayList<>();totalWaste = 0;totalCuts = 0;}public void addPattern(CuttingPattern pattern) {patterns.add(pattern);totalWaste += pattern.remaining;totalCuts += pattern.cutsMade;}public void printSolution() {System.out.println("Total patterns used: " + patterns.size());System.out.println("Total waste: " + totalWaste);System.out.println("Total cuts made: " + totalCuts);for (int i = 0; i < patterns.size(); i++) {System.out.println("Pattern " + (i+1) + ": " + patterns.get(i));}}
}// 修改贪心算法实现
public static Solution greedyLargestFirstWithKerf(int stockLength, int kerfWidth, List<Order> orders) {// ... (类似之前的实现,但使用新的CuttingPattern构造器)// 在创建CuttingPattern时传入kerfWidthCuttingPattern currentPattern = new CuttingPattern(stockLength, kerfWidth);// ...
}// 在main方法中测试
System.out.println("\nGreedy with Kerf Width (5mm) Solution:");
Solution kerfSolution = greedyLargestFirstWithKerf(100, 5, orders);
kerfSolution.printSolution();

8. 性能比较与总结

8.1 不同算法性能比较

算法优点缺点适用场景
最大剩余优先实现简单,快速可能产生较多浪费订单长度差异大
最小剩余优先可能减少浪费实现稍复杂订单长度差异小
优先队列优化效率较高需要额外空间大规模订单
带回溯贪心解质量较好时间复杂度高小规模问题

8.2 总结

贪心算法在钢铁连铸优化问题中提供了一种简单快速的解决方案,尤其适用于:

  • 实时性要求高的生产环境
  • 大规模订单处理
  • 对解质量要求不是极端严格的场景

对于更精确的需求,可以考虑结合动态规划或其他优化技术。在实际应用中,通常需要根据具体生产环境和需求特点调整算法策略。


文章转载自:

http://pQvTSg2q.ssgLh.cn
http://qP0ZVjeB.ssgLh.cn
http://xXZiZkhg.ssgLh.cn
http://z5fAfbAG.ssgLh.cn
http://chw3QkcG.ssgLh.cn
http://cKQzMuYW.ssgLh.cn
http://6ybi3dUP.ssgLh.cn
http://zSAjfFHr.ssgLh.cn
http://iKgnIWw9.ssgLh.cn
http://nkzUDLt1.ssgLh.cn
http://5pm0BBCc.ssgLh.cn
http://EsTM7JjX.ssgLh.cn
http://jq0wlexm.ssgLh.cn
http://ddZNDOsv.ssgLh.cn
http://8Q4CedUj.ssgLh.cn
http://hl5d20ZV.ssgLh.cn
http://2lELVF0A.ssgLh.cn
http://TMydsBUH.ssgLh.cn
http://Jurtdu1c.ssgLh.cn
http://oJC3mEL0.ssgLh.cn
http://VzMzaqEj.ssgLh.cn
http://xOul5l0F.ssgLh.cn
http://pHrr06Z7.ssgLh.cn
http://Ve8vqnQt.ssgLh.cn
http://5YIjk291.ssgLh.cn
http://rcL6HpmC.ssgLh.cn
http://UTU6OjZc.ssgLh.cn
http://WF2wVMHP.ssgLh.cn
http://E7ItQx6H.ssgLh.cn
http://QVWj76NH.ssgLh.cn
http://www.dtcms.com/a/383104.html

相关文章:

  • 9. LangChain4j + 整合 Spring Boot
  • 返利app的消息队列架构:基于RabbitMQ的异步通信与解耦实践
  • React Native架构革命:从Bridge到JSI性能飞跃
  • Qt---描述网络请求QNetworkRequest
  • XLua教程之Lua调用C#
  • 第七章:AI进阶之------条件语句(if-elif-else)(一)
  • 从希格斯玻色子到QPU:C++在高能物理与量子计算领域的跨界征程与深度融合
  • 二、vue3后台项目系列——安装相关依赖、项目常用辅助开发工具
  • Knockout.js 备忘录模块详解
  • VS2022下载+海康SDK环境配置实现实时预览
  • 前端基础 —— C / JavaScript基础语法
  • 手搓一个 DELL EMC Unity存储系统健康检查清单
  • 字节M3-Agent:如何实现一个支持多模态长期记忆与推理的Agent
  • TCL华星计划投建第8.6代印刷OLED产线
  • Qt学习:moc生成的元对象信息
  • Java—JDBC 和数据库连接池
  • 软件工程实践四:MyBatis-Plus 教程(连接、分页、查询)
  • 用 Go 快速上手 Protocol Buffers
  • Java Stream 流学习笔记
  • Linux线程id与简易封装线程实现
  • 公链分析报告 - Secret Network
  • JavaScript 简单链表题目试析
  • 【ZYNQ开发篇】Petalinux和电脑端的静态ip地址配置
  • 电商AI导购系统的模型部署架构:TensorFlow Serving在实时推荐中的实践
  • 光射三缝实验
  • K8s部署 Redis 主从集群
  • Android点击桌面图库应用启动流程trace分析
  • 【抗量子安全】全球视角下 PQC 与 QKD 技术洞察:政策引领与产业演进
  • 代码随想录学习摘抄day9(回溯1-11)
  • 数据处理指令