clear; clc; close all;
%% ---------------- 1. 问题数据定义(含缺陷信息) ----------------
% 订单信息矩阵:[订单量(套), 宽度目标(m), 高度目标(m), 单价(元/套)]
orders = [...
10, 1.6, 2.2, 480; % 订单1:学校教学楼
20, 1.8, 2.4, 680; % 订单2:酒店客房
20, 1.7, 2.3, 550; % 订单3:医院病房
15, 1.5, 2.0, 420]; % 订单4:政府办公楼
tol = 0.01; % 尺寸允许误差范围(±0.01m)types
% 计算宽度和高度的允许范围
width_ranges = [orders(:,2)-tol, orders(:,2)+tol];
height_ranges = [orders(:,3)-tol, orders(:,3)+tol];
% 整合所有边框类型的尺寸范围(8类:4宽+4高)
border_info = zeros(8,2);
for k = 1:size(orders,1)
border_info(2*k-1, :) = width_ranges(k, :); % 宽度类型(1,3,5,7)
border_info(2*k, :) = height_ranges(k, :); % 高度类型(2,4,6,8)
end
% 原材料信息(含缺陷):[长度(m), 单价(元/根)]
materials = [
5.5, 18; % 原材料1
6.2, 22; % 原材料2
7.8, 28]; % 原材料3
% 缺陷信息:每个单元格存储该类原材料的所有缺陷[起始位置, 长度]
defects = {
[1.0, 0.03]; % 原材料1的缺陷
[2.5, 0.04]; % 原材料1的缺陷
[0.5, 0.02]; % 原材料2的缺陷
[1.8, 0.05]; % 原材料2的缺陷
[3.0, 0.03]; % 原材料3的缺陷
};
% 计算各原材料的可用区间(去除缺陷区域)
available_segments = cell(3,1); % 存储每个原材料的可用区间[起始, 结束]
for m = 1:3
L = materials(m,1);
def = defects{m};
% 排序缺陷(按起始位置)
if ~isempty(def)
def = sortrows(def, 1);
% 计算缺陷结束位置
def(:,2) = def(:,1) + def(:,2);
end
% 生成可用区间
segs = [];
prev_end = 0;
for d = 1:size(def,1)
start_def = def(d,1);
end_def = def(d,2);
% 缺陷前的可用区间
if start_def > prev_end + 1e-12
segs = [segs; prev_end, start_def];
end
prev_end = end_def;
end
% 最后一个缺陷到末端的可用区间
if prev_end < L - 1e-12
segs = [segs; prev_end, L];
end
available_segments{m} = segs;
end
saw_width = 0.005; % 锯口宽度
max_cuts = 5; % 最大切割段数
% 材料需求总量
width_demand = 2 * orders(:,1);
height_demand = 2 * orders(:,1);
total_revenue = sum(orders(:,1) .* orders(:,4)); % 总收益
%% ---------------- 2. 生成含缺陷约束的切割模式 ----------------
patterns = []; % 存储所有可行切割模式
pidx = 0; % 模式编号
% 遍历每种原材料
for m = 1:size(materials,1)
L = materials(m,1);
segs = available_segments{m}; % 该原材料的可用区间
if isempty(segs)
continue; % 无可用区间,跳过
end
% 遍历每个可用区间
for s = 1:size(segs,1)
seg_start = segs(s,1);
seg_end = segs(s,2);
seg_length = seg_end - seg_start; % 可用区间长度
% 遍历可能的切割段数
for n = 1:max_cuts
% 生成切割类型组合
combos = multichoose(1:8, n);
for r = 1:size(combos,1)
types = combos(r, :);
% 计算最小总长度(按最小允许尺寸)+ 锯口损失
min_len = sum(border_info(types,1)) + (n-1) * saw_width;
% 检查是否能放入当前可用区间
if min_len <= seg_length + 1e-12
pidx = pidx + 1;
patterns(pidx).material = m; % 原材料类型
patterns(pidx).segment = s; % 可用区间编号
patterns(pidx).segment_info = [seg_start, seg_end]; % 区间范围
% 统计各类型边框数量
cnt = zeros(1,8);
for t = types
cnt(t) = cnt(t) + 1;
end
patterns(pidx).counts = cnt; % 各类型数量
patterns(pidx).n = n; % 切割段数
patterns(pidx).types = types; % 边框类型列表
patterns(pidx).seg_length = seg_length; % 可用区间长度
end
end
end
end
end
num_patterns = length(patterns);
fprintf('共生成 %d 种可行切割模式(考虑缺陷)\n', num_patterns);
%% ---------------- 3. 建立整数规划模型并求解 ----------------
% 转换模式信息为矩阵
all_patterns = zeros(num_patterns, 11); % 增加一列存储可用区间编号
for i = 1:num_patterns
all_patterns(i,1) = patterns(i).material; % 原材料类型
all_patterns(i,2) = patterns(i).segment; % 可用区间编号
all_patterns(i,3:10) = patterns(i).counts; % 8类边框数量
all_patterns(i,11) = patterns(i).n; % 切割段数
end
% 目标函数:最小化总成本
f = zeros(num_patterns,1);
for i = 1:num_patterns
m = all_patterns(i,1);
f(i) = materials(m,2); % 原材料单价
end
% 约束矩阵:满足各类型边框需求
A = all_patterns(:, 3:10)'; % 8类边框的产量矩阵
b = zeros(8,1);
for k = 1:4
b(2*k-1) = width_demand(k); % 宽度需求
b(2*k) = height_demand(k); % 高度需求
end
% 整数规划参数
intcon = 1:num_patterns; % 整数变量
lb = zeros(num_patterns,1); % 下界
ub = inf(num_patterns,1); % 上界
% 求解
options = optimoptions('intlinprog', 'Display', 'off');
[x, exitflag] = intlinprog(f, intcon, -A, -b, [], [], lb, ub, options);
if exitflag <= 0
warning('intlinprog 未找到最优解,进行手动调整');
end
% 处理求解结果
x = round(x);
produced = A * x;
% 确保满足需求
if any(produced < b)
deficit = b - produced;
while any(deficit > 0)
j = find(deficit > 0, 1);
candidates = find(all_patterns(:,2+j) > 0);
if ~isempty(candidates)
best_p = candidates(1);
x(best_p) = x(best_p) + 1;
produced = A * x;
deficit = b - produced;
else
error('无可用模式满足需求,请检查参数');
end
end
end
used_patterns = find(x > 0);
%% ---------------- 4. 统计结果与指标计算 ----------------
total_saw_loss = 0; % 总锯口损失
total_cut_length = 0; % 总切割长度
material_usage = zeros(size(materials,1),1); % 原材料使用根数
total_defect_length = 0; % 缺陷导致的损失长度
% 计算每种原材料的总缺陷长度
material_defect_length = zeros(3,1);
for m = 1:3
def = defects{m};
material_defect_length(m) = sum(def(:,2)); % 缺陷总长度
end
% 遍历所有使用的模式
for i = used_patterns'
cnt_inst = x(i); % 使用次数
pat = patterns(i);
m = pat.material; % 原材料类型
L = materials(m,1);
ncuts = pat.n; % 切割段数
types_template = pat.types;
seg_length = pat.seg_length; % 可用区间长度
% 累加原材料使用量和缺陷损失
material_usage(m) = material_usage(m) + cnt_inst;
total_defect_length = total_defect_length + cnt_inst * material_defect_length(m);
% 计算每根的切割细节
for inst = 1:cnt_inst
[seg_lengths, leftover] = fit_segment_lengths(types_template, orders, border_info, seg_length, saw_width);
total_cut_length = total_cut_length + sum(seg_lengths);
total_saw_loss = total_saw_loss + (ncuts - 1) * saw_width;
end
end
% 核心指标计算
total_material_length = sum(material_usage .* materials(:,1)); % 原材料总长度
total_used_effective = total_cut_length + total_saw_loss; % 有效使用长度(含锯口)
total_remaining = total_material_length - (total_used_effective + total_defect_length); % 余料
utilization = total_used_effective / total_material_length; % 利用率(有效长度/总长度)
loss_rate = 1 - utilization; % 总损失率
% 经济指标
total_cost = material_usage' * materials(:,2); % 总成本
profit = total_revenue - total_cost; % 总利润
%% ---------------- 5. 输出结果 ----------------
fprintf('\n===== 含缺陷的最优切割方案结果 =====\n');
fprintf('总收益: %.2f 元\n', total_revenue);
fprintf('总成本: %.2f 元\n', total_cost);
fprintf('总利润: %.2f 元\n', profit);
fprintf('总原材料长度: %.2f 米\n', total_material_length);
fprintf('有效使用长度: %.2f 米\n', total_used_effective);
fprintf('缺陷损失长度: %.2f 米\n', total_defect_length);
fprintf('余料长度: %.2f 米\n', total_remaining);
fprintf('利用率: %.2f%%\n', utilization*100);
fprintf('损失率: %.2f%%\n', loss_rate*100);
%% ---------------- 6. 可视化展示 ----------------
% 图1:原材料使用数量
figure;
bar(material_usage);
xticklabels(arrayfun(@(v) sprintf('%.1fm',v), materials(:,1), 'UniformOutput', false));
ylabel('使用根数');
title('各类原材料使用数量(含缺陷)');
grid on;
% 图2:材料分配比例(含缺陷)
figure;
pie([total_cut_length, total_saw_loss, total_defect_length, max(total_remaining,0)], ...
{'边框总长度','锯缝损失','缺陷损失','余料'});
title('材料分配比例(含缺陷)');
% 图3:切割模式示意图(标注缺陷)
colors = lines(8); % 边框类型颜色
figure('Name','含缺陷的切割模式示意图','Position',[100,100,1400,900]);
y_gap = 1.5;
y_pos = 0;
% 按原材料分组展示
for m = 1:3
L = materials(m,1);
def = defects{m};
segs = available_segments{m};
% 筛选该原材料的使用模式
m_patterns = used_patterns(all_patterns(used_patterns,1) == m);
% 绘制原材料标题
title_text = sprintf('原材料 %.1fm(含缺陷)', L);
text(L/2, y_pos + 1.5, title_text, 'FontWeight','bold', 'FontSize',12, ...
'HorizontalAlignment','center', 'VerticalAlignment','bottom');
y_pos = y_pos + 2;
% 绘制原材料基线和缺陷
% 基线
plot([0, L], [y_pos, y_pos], 'k-', 'LineWidth',1);
% 缺陷区域
for d = 1:size(def,1)
def_start = def(d,1);
def_len = def(d,2);
def_end = def_start + def_len;
patch([def_start, def_end, def_end, def_start], ...
[y_pos-0.2, y_pos-0.2, y_pos+0.2, y_pos+0.2], [1,0.3,0.3], ...
'EdgeColor','k', 'FaceAlpha',0.7); % 红色标注缺陷
end
y_pos = y_pos + 1;
% 绘制可用模式
if ~isempty(m_patterns)
for p_idx = 1:length(m_patterns)
pat_id = m_patterns(p_idx);
pat = patterns(pat_id);
seg_idx = pat.segment;
seg = segs(seg_idx,:);
seg_start = seg(1);
seg_end = seg(2);
% 绘制模式标题
text(seg_start-0.1, y_pos+0.5, sprintf('模式 %d (×%d)', pat_id, x(pat_id)), ...
'HorizontalAlignment','right', 'FontSize',9);
% 绘制切割段
xpos = seg_start;
for s = 1:length(pat.types)
t = pat.types(s);
% 确定边框长度
if mod(t-1,2) == 0
seg_len = orders(floor((t-1)/2)+1, 2);
else
seg_len = orders(floor((t-1)/2)+1, 3);
end
% 绘制边框段
patch([xpos, xpos+seg_len, xpos+seg_len, xpos], ...
[y_pos-0.3, y_pos-0.3, y_pos+0.3, y_pos+0.3], colors(t,:), ...
'EdgeColor','k');
xpos = xpos + seg_len;
% 绘制锯口
if s < length(pat.types)
patch([xpos, xpos+saw_width, xpos+saw_width, xpos], ...
[y_pos-0.3, y_pos-0.3, y_pos+0.3, y_pos+0.3], [0.4 0.4 0.4], ...
'EdgeColor','none', 'FaceAlpha',0.4);
xpos = xpos + saw_width;
end
end
y_pos = y_pos + y_gap;
end
else
text(L/2, y_pos, '无使用模式', 'HorizontalAlignment','center', 'Color',[0.6 0.6 0.6]);
y_pos = y_pos + 2;
end
y_pos = y_pos + 2; % 组间间隔
end
% 坐标轴设置
xlim([0, max(materials(:,1))+0.5]);
ylim([0, y_pos]);
xlabel('长度 (米)');
ylabel('切割模式');
title('含缺陷原材料的切割模式示意图(红色为缺陷区域)');
% 图例
hold on;
h_legend = gobjects(9,1);
for t = 1:8
h_legend(t) = plot(nan, nan, 's', 'MarkerFaceColor', colors(t,:), ...
'MarkerEdgeColor','k', 'MarkerSize', 10);
end
h_legend(9) = plot(nan, nan, 's', 'MarkerFaceColor', [1,0.3,0.3], ...
'MarkerEdgeColor','k', 'MarkerSize', 10);
legend(h_legend, {'订单1宽','订单1高','订单2宽','订单2高','订单3宽','订单3高','订单4宽','订单4高','缺陷区域'}, ...
'Location','eastoutside');
grid on;
%% ---------------- 辅助函数 ----------------
% 生成可重复组合
function combos = multichoose(pool, n)
if n == 0
combos = [];
return;
end
if n == 1
combos = pool(:);
return;
end
combos = [];
for i = 1:length(pool)
head = pool(i);
tail_pool = pool(i:end);
sub = multichoose(tail_pool, n-1);
combos = [combos; [repmat(head, size(sub,1), 1), sub]];
end
end
% 计算切割段实际长度
function [seg_lengths, leftover] = fit_segment_lengths(types, orders, border_info, seg_length, saw_width)
n = length(types);
targets = zeros(1,n);
mins = zeros(1,n);
for k = 1:n
t = types(k);
order_idx = floor((t-1)/2)+1;
if mod(t-1,2) == 0
targets(k) = orders(order_idx, 2);
else
targets(k) = orders(order_idx, 3);
end
mins(k) = border_info(t,1);
end
kerf = (n - 1) * saw_width;
total_target = sum(targets);
if total_target + kerf <= seg_length + 1e-12
seg_lengths = targets;
leftover = seg_length - (sum(seg_lengths) + kerf);
return;
end
need = total_target + kerf - seg_length;
allowed_reduction = targets - mins;
seg_lengths = targets;
[sorted_vals, idxs] = sort(allowed_reduction, 'descend');
rem = need;
for ii = 1:length(idxs)
j = idxs(ii);
red = min(sorted_vals(ii), rem);
seg_lengths(j) = seg_lengths(j) - red;
rem = rem - red;
if rem <= 1e-12
break;
end
end
leftover = seg_length - (sum(seg_lengths) + kerf);
if leftover < 0 && leftover > -1e-8
leftover = 0;
end
end
这个代码运行不出来了,帮我改一下
最新发布