题目如下
(发送该博客时间已经超过比赛时间,若有违反协议,请私信我删除)
某电商公司为降低物流成本并提升配送效率,需优化其快递分配策略,以求达到快递配送费用最省。公司根据包裹重量将订单划分为若干区间(如0-0.1kg、0.11-0.2kg等)(见表2),每个区间只能选择一家快递公司承运。不同快递公司在各重量区间的报价(见表1)。此外,市场环境动态变化(如运费调整、订单量波动、重量分布变化),需建立灵活的调整机制。请根据表1、表2数据及相关背景,完成下面2个问题:
- 请给出优化算法模型和流程图,并给出各重量段最优快递商分配策略;
- 随着市场环境的变化(如运费调整、订单量波动、重量分布变化),提出相应的调整策略(比如:当市场波动超过阈值(如±5%)时,提出再优化触发机制),并作灵敏度分析。
表1:备选快递费用
快递 | 费用 |
广东极兔 | 总均重0.8kg 2.2元,每增加0.1kg加0.16元 |
广东韵达 | 总均重0.6kg 2.2元,每增加0.1kg加0.13元 |
安徽中通 | 单重0-0.5kg 1.8元,0.51-1kg 2元,1.01-2kg 3元,2.01-3kg 3.4元 |
安徽韵达 | 单重0-0.5kg 1.6元,0.51-1kg 2.1元,1.01-2kg 2.8元,2.01-3kg 3.4元 |
安徽申 通 | 单重0-0.5kg 1.7元,0.51-1kg 2元,1.01-2kg 2.9元,2.01-3kg 3.45元 |
表2 目前电商公司每月各重量段单数和重量
重量段 | 单数 | 重量 |
0.00-0.1kg | 69700 | 2353.91 |
0.11-0.2kg | 57481 | 8182.675 |
0.21-0.3kg | 56488 | 14054.609 |
0.31-0.4kg | 35921 | 12922.021 |
0.41-0.5kg | 49474 | 22938.796 |
0.51-0.6kg | 18838 | 10469.116 |
0.61-0.7kg | 36880 | 25183.539 |
0.71-0.8kg | 30888 | 21976.825 |
0.81-0.9kg | 17089 | 14837.163 |
0.91-1.0kg | 18751 | 17985.321 |
1.01-1.1kg | 16246 | 17353.769 |
1.11-1.2kg | 15747 | 17659.223 |
1.21-1.3kg | 9875 | 12434.107 |
1.31-1.4kg | 17037 | 23326.637 |
1.41-1.5kg | 7621 | 11220.825 |
1.51-1.6kg | 10285 | 15793.204 |
1.61-1.7kg | 8917 | 14893.159 |
1.71-1.8kg | 7230 | 12616.81 |
1.81-1.9kg | 11384 | 21475.125 |
1.91-2.0kg | 5552 | 10930.969 |
2.01-2.5kg | 27178 | 61033.246 |
2.51-3.0kg | 19794 | 50752.39 |
以下是题解过程(除去重量变化)
第一大题
DeliveryCosts 类
public class DeliveryCosts {
// 计算广东极兔的运费
public double jituGuangdong(double weight) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
double baseCost = 2.2;
double additionalWeight = weight - 0.8;
if (additionalWeight > 0) {
return Double.parseDouble(String.format("%.2f", baseCost + (Math.ceil(additionalWeight / 0.1) * 0.16)));//返回保留两位小数
}
return baseCost;
}
// 计算广东韵达的运费,返回保留两位小数
public double yundaGuangdong(double weight) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
double baseCost = 2.2;
double additionalWeight = weight - 0.6;
if (additionalWeight > 0) {
return Double.parseDouble(String.format("%.2f", baseCost + (Math.ceil(additionalWeight / 0.1) * 0.13)));//返回保留两位小数
}
return baseCost;
}
// 计算安徽中通的运费
public double zhongtongAnhui(double weight) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.8;
} else if (weight <= 1) {
return 2.0;
} else if (weight <= 2) {
return 3.0;
} else if (weight <= 3) {
return 3.4;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
// 计算安徽韵达的运费
public double yundaAnhui(double weight) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.6;
} else if (weight <= 1) {
return 2.1;
} else if (weight <= 2) {
return 2.8;
} else if (weight <= 3) {
return 3.4;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
// 计算安徽申通的运费
public double shentongAnhui(double weight) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.7;
} else if (weight <= 1) {
return 2.0;
} else if (weight <= 2) {
return 2.9;
} else if (weight <= 3) {
return 3.45;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
}
-DeliveryCosts类存放的是一些函数,是针对表1进行计算单个费用,每一个函数的返回值都是double,
并且前两个函数的输出进行了保留两位小数的操作。每一个函数都留有报错的情况分析,比如输入重量小于
最小值或者大于最大值都会弹窗警告。这个类最后留有检测用主函数,需要的话可以去掉注释
WeightSegment类
public class WeightSegment {
private String range;
private int orderCount;
private double weight;
public WeightSegment(String range, int orderCount, double weight) {
this.range = range;
this.orderCount = orderCount;
this.weight = weight;
}
public String getRange() {
return range;
}
public int getOrderCount() {
return orderCount;
}
public double getWeight() {
return weight;
}
}
-WeightSegment类是创建了一个对象,这个类主要实现表2数据的分类,表2数据在Main类中进行存放,3种数据的
类型分别为String range; int orderCount; double weight;,方便在后续计算中调用
Main类
import java.util.*;
public class Main {
public static void main(String[] args) {
DeliveryCosts deliveryCosts = new DeliveryCosts();
// 表2中的重量段、单数和重量
List<WeightSegment> weightSegments = Arrays.asList(
new WeightSegment("0.00-0.1kg", 69700, 2353.91),
new WeightSegment("0.11-0.2kg", 57481, 8182.675),
new WeightSegment("0.21-0.3kg", 56488, 14054.609),
new WeightSegment("0.31-0.4kg", 35921, 12922.021),
new WeightSegment("0.41-0.5kg", 49474, 22938.796),
new WeightSegment("0.51-0.6kg", 18838, 10469.116),
new WeightSegment("0.61-0.7kg", 36880, 25183.539),
new WeightSegment("0.71-0.8kg", 30888, 21976.825),
new WeightSegment("0.81-0.9kg", 17089, 14837.163),
new WeightSegment("0.91-1.0kg", 18751, 17985.321),
new WeightSegment("1.01-1.1kg", 16246, 17353.769),
new WeightSegment("1.11-1.2kg", 15747, 17659.223),
new WeightSegment("1.21-1.3kg", 9875, 12434.107),
new WeightSegment("1.31-1.4kg", 17037, 23326.637),
new WeightSegment("1.41-1.5kg", 7621, 11220.825),
new WeightSegment("1.51-1.6kg", 10285, 15793.204),
new WeightSegment("1.61-1.7kg", 8917, 14893.159),
new WeightSegment("1.71-1.8kg", 7230, 12616.81),
new WeightSegment("1.81-1.9kg", 11384, 21475.125),
new WeightSegment("1.91-2.0kg", 5552, 10930.969),
new WeightSegment("2.01-2.5kg", 27178, 61033.246),
new WeightSegment("2.51-3.0kg", 19794, 50752.39)
);
// 计算每个重量段的最优快递公司
for (WeightSegment segment : weightSegments) {
double averageWeight = segment.getWeight() / segment.getOrderCount();
double jituGuangdongCost = deliveryCosts.jituGuangdong(averageWeight) * segment.getOrderCount();
double yundaGuangdongCost = deliveryCosts.yundaGuangdong(averageWeight) * segment.getOrderCount();
double zhongtongAnhuiCost = deliveryCosts.zhongtongAnhui(averageWeight) * segment.getOrderCount();
double yundaAnhuiCost = deliveryCosts.yundaAnhui(averageWeight) * segment.getOrderCount();
double shentongAnhuiCost = deliveryCosts.shentongAnhui(averageWeight) * segment.getOrderCount();
// 找出最低成本的快递公司
Map<String, Double> costs = new HashMap<>();
costs.put("广东极兔", jituGuangdongCost);
costs.put("广东韵达", yundaGuangdongCost);
costs.put("安徽中通", zhongtongAnhuiCost);
costs.put("安徽韵达", yundaAnhuiCost);
costs.put("安徽申通", shentongAnhuiCost);
// 使用 Stream API 找到最低成本的快递公司
String bestCompany = costs.entrySet().stream()
.min(Map.Entry.comparingByValue())
.orElseThrow(() -> new IllegalStateException("没有找到最低成本的快递公司"))
.getKey();
double bestCost = costs.get(bestCompany);
// 格式化总运费,保留两位小数
double formattedBestCost = Double.parseDouble(String.format("%.2f", bestCost));
System.out.println("重量段: " + segment.getRange() + ", 最佳快递公司: " + bestCompany + ", 总运费: " + formattedBestCost);
}
}
}
这个类是主要的计算逻辑,首先在主函数中创建DeliveryCosts对象deliveryCosts,用来调用计算费用的函数
然后再创建链表,链表类型为WeightSegment,用来继承这个类里面的分类,
接着逐一存放 表2 的信息
看到下面的for循环,这是一个增强循环,用来调用上面链表的每一次存放的信息,averageWeight 为每一个重量段中的平均价格
double jituGuangdongCost = deliveryCosts.jituGuangdong(averageWeight) * segment.getOrderCount();
以这一行代码为例
新建了一个double类型的数字,
deliveryCosts.jituGuangdong();调用计算广东极兔价格的函数,在括号里面传入重量,
segment.getOrderCount();取出当前循环下(当前重量段下)的单数,
相乘得到当前重量段下,选择这个快递公司的总价,赋值给jituGuangdongCost
接下来解释如何找到成本最低的快递公司
HashMap哈希表,这个表需要指定两个数据类型,第一个String是key,第二个Double是Value(数据)
以快递名称作为key,快递单价作为value,存入哈希表costs;
用java自带的Stream流的API直接查找哈希表中最小的value,输出这个value对应的key
double bestCost = costs.get(bestCompany);通过key找到对应的值
为了这个运费在最后输出时格式正确(因为double精度问题,会出现32421.0000000000002的情况)还需要格式化输出一次,保留两位小数
最后在控制栏打印每个重量段的最佳快递公司,以及总运费
第二大题(运费调整)
这一题不作为详情介绍,主要是在第一题的基础上修改DeliveryCosts类的参数
主要逻辑:在主函数中创建Scanner对象,来获取传入的价格浮动,传给DeliveryCosts中的每一个函数
代码如下
DeliveryCosts类
public class DeliveryCosts {
// 计算广东极兔的运费
public double jituGuangdong(double weight,double up1 ,double up2) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
double baseCost = 2.2+up1;
double additionalWeight = weight - 0.8;
if (additionalWeight > 0) {
return Double.parseDouble(String.format("%.2f", baseCost + (Math.ceil(additionalWeight / 0.1) * (0.16+up2))));//返回保留两位小数
}
return baseCost;
}
// 计算广东韵达的运费,返回保留两位小数
public double yundaGuangdong(double weight,double up1 ,double up2) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
double baseCost = 2.2+up1;
double additionalWeight = weight - 0.6;
if (additionalWeight > 0) {
return Double.parseDouble(String.format("%.2f", baseCost + (Math.ceil(additionalWeight / 0.1) * (0.13+up2))));//返回保留两位小数
}
return baseCost;
}
// 计算安徽中通的运费
public double zhongtongAnhui(double weight,double up) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.8+up;
} else if (weight <= 1) {
return 2.0+up;
} else if (weight <= 2) {
return 3.0+up;
} else if (weight <= 3) {
return 3.4+up;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
// 计算安徽韵达的运费
public double yundaAnhui(double weight,double up) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.6+up;
} else if (weight <= 1) {
return 2.1+up;
} else if (weight <= 2) {
return 2.8+up;
} else if (weight <= 3) {
return 3.4+up;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
// 计算安徽申通的运费
public double shentongAnhui(double weight,double up) {
if (weight <= 0) {
throw new IllegalArgumentException("重量必须大于0");
}
if (weight <= 0.5) {
return 1.7+up;
} else if (weight <= 1) {
return 2.0+up;
} else if (weight <= 2) {
return 2.9+up;
} else if (weight <= 3) {
return 3.45+up;
} else {
throw new IllegalArgumentException("重量超出范围");
}
}
}
Main类
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
System.out.println("请输入广东极兔快递上涨的基本价格");
double jutuGuangdongUp1=in.nextDouble();//上涨的基本价格
System.out.println("请输入广东极兔快递上涨的每段价格");
double jituGuangdongUp2=in.nextDouble();//上涨的每段价格
System.out.println("请输入广东韵达快递上涨的基本价格");
double yudaGuangdongUp1=in.nextDouble();//上涨的基本价格
System.out.println("请输入广东韵达快递上涨的每段价格");
double yudaGuangdongUp2=in.nextDouble();//上涨的每段价格
System.out.println("请输入安徽中通快递上涨的每段价格");
double zhongtongAnhuiUp=in.nextDouble();
System.out.println("请输入安徽韵达快递上涨的每段价格");
double yundaAnhuiUp=in.nextDouble();
System.out.println("请输入安徽申通快递上涨的每段价格");
double shengtongAnhuiUp=in.nextDouble();
DeliveryCosts deliveryCosts = new DeliveryCosts();
// 表2中的重量段、单数和重量
List<WeightSegment> weightSegments = Arrays.asList(
new WeightSegment("0.00-0.1kg", 69700, 2353.91),
new WeightSegment("0.11-0.2kg", 57481, 8182.675),
new WeightSegment("0.21-0.3kg", 56488, 14054.609),
new WeightSegment("0.31-0.4kg", 35921, 12922.021),
new WeightSegment("0.41-0.5kg", 49474, 22938.796),
new WeightSegment("0.51-0.6kg", 18838, 10469.116),
new WeightSegment("0.61-0.7kg", 36880, 25183.539),
new WeightSegment("0.71-0.8kg", 30888, 21976.825),
new WeightSegment("0.81-0.9kg", 17089, 14837.163),
new WeightSegment("0.91-1.0kg", 18751, 17985.321),
new WeightSegment("1.01-1.1kg", 16246, 17353.769),
new WeightSegment("1.11-1.2kg", 15747, 17659.223),
new WeightSegment("1.21-1.3kg", 9875, 12434.107),
new WeightSegment("1.31-1.4kg", 17037, 23326.637),
new WeightSegment("1.41-1.5kg", 7621, 11220.825),
new WeightSegment("1.51-1.6kg", 10285, 15793.204),
new WeightSegment("1.61-1.7kg", 8917, 14893.159),
new WeightSegment("1.71-1.8kg", 7230, 12616.81),
new WeightSegment("1.81-1.9kg", 11384, 21475.125),
new WeightSegment("1.91-2.0kg", 5552, 10930.969),
new WeightSegment("2.01-2.5kg", 27178, 61033.246),
new WeightSegment("2.51-3.0kg", 19794, 50752.39)
);
// 计算每个重量段的最优快递公司
for (WeightSegment segment : weightSegments) {
double averageWeight = segment.getWeight() / segment.getOrderCount();
double jituGuangdongCost = deliveryCosts.jituGuangdong(averageWeight,jutuGuangdongUp1,jituGuangdongUp2) * segment.getOrderCount();
double yundaGuangdongCost = deliveryCosts.yundaGuangdong(averageWeight,yudaGuangdongUp1,yudaGuangdongUp2) * segment.getOrderCount();
double zhongtongAnhuiCost = deliveryCosts.zhongtongAnhui(averageWeight,zhongtongAnhuiUp) * segment.getOrderCount();
double yundaAnhuiCost = deliveryCosts.yundaAnhui(averageWeight,yundaAnhuiUp) * segment.getOrderCount();
double shentongAnhuiCost = deliveryCosts.shentongAnhui(averageWeight,shengtongAnhuiUp) * segment.getOrderCount();
// 找出最低成本的快递公司
Map<String, Double> costs = new HashMap<>();
costs.put("广东极兔", jituGuangdongCost);
costs.put("广东韵达", yundaGuangdongCost);
costs.put("安徽中通", zhongtongAnhuiCost);
costs.put("安徽韵达", yundaAnhuiCost);
costs.put("安徽申通", shentongAnhuiCost);
// 使用 Stream API 找到最低成本的快递公司
String bestCompany = costs.entrySet().stream()
.min(Map.Entry.comparingByValue())
.orElseThrow(() -> new IllegalStateException("没有找到最低成本的快递公司"))
.getKey();
double bestCost = costs.get(bestCompany);
// 格式化总运费,保留两位小数
double formattedBestCost = Double.parseDouble(String.format("%.2f", bestCost));
System.out.println("重量段: " + segment.getRange() + ", 最佳快递公司: " + bestCompany + ", 总运费: " + formattedBestCost);
}
}
}
WeightSegment类没有做修改
第二大题(订单量调整)--重点研究对象
为了完成这个题目,原先的代码就显得太过于臃肿,不符合java的封装设计,对此,我们先对代码进行重构
目前不会变换的有DeliveryCosts类(同1),WeightSegment类(同1)
我们将计算最佳公司的函数单独写成一个类BestCom,这个类实现一个函数countBestCompanies,返回值为数组,这个数组是为了存放对应快递公司在这些重量段中所分得的单数,以便后续绘图处理
BestCom类
import java.util.*;
public class BestCom {
// 计算每个重量段的最优快递公司
public int[] countBestCompanies(List<WeightSegment> segments) {
int[] data = {0, 0, 0, 0, 0};
DeliveryCosts deliveryCosts = new DeliveryCosts();
for (WeightSegment segment : segments) {
double averageWeight = segment.getWeight() / segment.getOrderCount();
double jituGuangdongCost = deliveryCosts.jituGuangdong(averageWeight) * segment.getOrderCount();
double yundaGuangdongCost = deliveryCosts.yundaGuangdong(averageWeight) * segment.getOrderCount();
double zhongtongAnhuiCost = deliveryCosts.zhongtongAnhui(averageWeight) * segment.getOrderCount();
double yundaAnhuiCost = deliveryCosts.yundaAnhui(averageWeight) * segment.getOrderCount();
double shentongAnhuiCost = deliveryCosts.shentongAnhui(averageWeight) * segment.getOrderCount();
Map<String, Double> costs = new HashMap<>();
costs.put("广东极兔", jituGuangdongCost);
costs.put("广东韵达", yundaGuangdongCost);
costs.put("安徽中通", zhongtongAnhuiCost);
costs.put("安徽韵达", yundaAnhuiCost);
costs.put("安徽申通", shentongAnhuiCost);
String bestCompany = costs.entrySet().stream()
.min(Map.Entry.comparingByValue())
.orElseThrow(() -> new IllegalStateException("没有找到最低成本的快递公司"))
.getKey();
if ("广东极兔".equals(bestCompany)) {
data[0]+=segment.getOrderCount();
} else if ("广东韵达".equals(bestCompany)) {
data[1]+=segment.getOrderCount();
} else if ("安徽中通".equals(bestCompany)) {
data[2]+=segment.getOrderCount();
} else if ("安徽韵达".equals(bestCompany)) {
data[3]+=segment.getOrderCount();
} else if ("安徽申通".equals(bestCompany)) {
data[4]+=segment.getOrderCount();
}
}
return data;
}
}
既然说到了绘图处理,那肯定要新建一个Windows类来绘图,
这个类只有一个showUI方法,先创建一个窗体fream,再在这个窗体上创建面板panel
对这个面板获取画笔,调用父类的 paintComponent 方法,然后在面板上进行绘制操作
这个函数没有返回值,直接生成一个界面
Windows类
import javax.swing.*;
import java.awt.*;
public class Windows {
private int[] data;
private String[] labels = {"广东极兔", "广东韵达", "安徽中通", "安徽韵达", "安徽申通"}; // 矩形下方的文字标签
public void showUI(int[] data) {
this.data = data;
JFrame frame = new JFrame("订单量波动情况图表");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
// 创建一个自定义的 JPanel
JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // 调用父类的 paintComponent 方法
g.setColor(Color.BLACK); // 设置绘制颜色为黑色
g.fillRect(20, 480, 480, 2); // 绘制水平线
g.fillRect(40, 20, 2, 500); // 绘制垂直线
// 计算最大值和缩放比例
int maxData = 0;
for (int value : data) {
if (value > maxData) {
maxData = value;
}
}
double scale = 460.0 / maxData; // 将数据缩放到460像素的高度
// 绘制刻度
int numTicks = 10; // 刻度数量
for (int i = 0; i <= numTicks; i++) {
int y = 480 - (int) (i * (460.0 / numTicks));
g.drawLine(40, y, 50, y); // 绘制刻度线
g.drawString(String.valueOf((int) (i * (maxData / numTicks))), 10, y); // 绘制刻度值
}
// 绘制矩形
int startX = 60; // 水平线的起始x坐标
int barWidth = 50; // 每个矩形的宽度
int space = 30; // 矩形之间的间距
for (int i = 0; i < data.length; i++) {
int height = (int) (data[i] * scale); // 缩放后的高度
int x = startX + i * (barWidth + space); // 当前矩形的x坐标
int y = 480 - height; // 当前矩形的y坐标(从水平线向上绘制)
g.fillRect(x, y, barWidth, height); // 绘制矩形
// 在矩形下方绘制标签
g.drawString(labels[i], x, 520); // 标签的y坐标设置为520
// 在矩形上方绘制数值
g.drawString(String.valueOf(data[i]), x, y - 5); // 数值的y坐标设置为矩形顶部上方5像素
}
}
};
frame.add(panel); // 将自定义的 JPanel 添加到 JFrame 中
frame.setVisible(true);
}
}
先写一段测试代码检查效果
public static void main(String[] args) {
Windows window = new Windows();
int[] data = {100000, 5000, 3000, 52000, 60000};
window.showUI(data);
}
效果如图
再接着就是我们这一题的核心要点,如何实习订单波动
首先要考虑的是,订单波动方向,是小重量?平均重量?大重量,这可以作为订单波动的三大类
再就是波动情况,订单的波动有强有弱,上涨10%,20%都有可能,这就要作为第二大因素
了解了这个两个因素,就可以着手写这个波动的函数了
Fluctuate类--关键
先给代码
import java.util.ArrayList;
import java.util.List;
public class Fluctuate {
public List<WeightSegment> fluctuateData(List<WeightSegment> segments, String direction, double fluctuationPercentage) {
List<WeightSegment> fluctuatedSegments = new ArrayList<>();
for (WeightSegment segment : segments) {
int orderCount = segment.getOrderCount();
double weight = segment.getWeight();
// 根据波动方向调整订单数量
if ("小重量".equals(direction)) {
if (segment.getRange().contains("0.00") || segment.getRange().contains("0.11") || segment.getRange().contains("0.21")|| segment.getRange().contains("0.31")
|| segment.getRange().contains("0.41")) {
orderCount = (int) Math.round(orderCount * (1 + fluctuationPercentage / 100.0));
} else {
orderCount = (int) Math.round(orderCount * (1 - fluctuationPercentage/ 100.0));
}
} else if ("平均重量".equals(direction)) {
if (segment.getRange().contains("0.61") || segment.getRange().contains("0.71")|| segment.getRange().contains("0.81")|| segment.getRange().contains("0.91")
|| segment.getRange().contains("1.01")|| segment.getRange().contains("1.11")|| segment.getRange().contains("1.21")|| segment.getRange().contains("1.31")
|| segment.getRange().contains("1.41")) {
orderCount = (int) Math.round(orderCount * (1 + fluctuationPercentage / 100.0));
} else {
orderCount = (int) Math.round(orderCount *(1 - fluctuationPercentage/ 100.0));
}
} else if ("大重量".equals(direction)) {
if (segment.getRange().contains("1.81") || segment.getRange().contains("1.91")||segment.getRange().contains("2.01") || segment.getRange().contains("2.51")
) {
orderCount = (int) Math.round(orderCount * (1 + fluctuationPercentage / 100.0));
} else {
orderCount = (int) Math.round(orderCount *(1 - fluctuationPercentage/ 100.0));
}
}
// 重新计算总重量
double newWeight = weight / segment.getOrderCount() * orderCount;
fluctuatedSegments.add(new WeightSegment(segment.getRange(), orderCount, newWeight));
System.out.println(segment.getRange()+" "+orderCount+" "+newWeight);
}
return fluctuatedSegments;
}
}
这个类中只有一个函数fluctuateData,这个函数要传入3个参数,分别是:原始数据的链表,波动方向,波动幅度
使用增强循环,遍历原始数据链表中的每一行数据,
用equals方法读取控制台的文字,判断波动方向,执行对应操作
在对每一行数据执行.getRange().contains()方法,作为检测重量段,如
if ("小重量".equals(direction)) {
if (segment.getRange().contains("0.00") || segment.getRange().contains("0.11") || segment.getRange().contains("0.21")|| segment.getRange().contains("0.31")
|| segment.getRange().contains("0.41")) {
orderCount = (int) Math.round(orderCount * (1 + fluctuationPercentage / 100.0));
} else {
orderCount = (int) Math.round(orderCount * (1 - fluctuationPercentage/ 100.0));
}
}
这里就分出来5个数量段作为小重量,执行单数增加的操作,其他则执行单数减少
对于这5个分段,数值会向上波动
orderCount(波动)=orderCount(原始)*(1 + fluctuationPercentage / 100.0)
(fluctuationPercentage为百分制,如10,20)
对于这五个分段之外的数据,数值会向下波动,
orderCount(波动)=orderCount(原始)*(1 – fluctuationPercentage / 100.0)
剩下两个分段同理
这三种操作在同一个循环中实现,也就是说,一次只取出原始数据矩阵中的一行来操作
Range1 | OrderCount1 | Weight1 |
Range2 | OrderCount2 | Weight2 |
…… | …… | …… |
Range+i | OrderCount+i | Weight+i |
每一次循环,取出Ordercount+i i->[1,22]
检测这一行的range是否在选择重量的分段中(小重量/平均重量/大重量)
然后执行对应的分段计算
最后重算重量newWeight = weight /OrderCount(波动前) * orderCount(波动后)
将新的数据写入新的矩阵之中(例如现在处理完了两行)
Range1 | OrderCount1(波动后) | newWeight1 |
Range2 | OrderCount2(波动后) | newWeight2 |
这个计算波动的操作会循环到原始数据全部计算完成
Main类
最后来看主函数的调度
import java.util.*;
public class Main {
public static void main(String[] args) {
BestCom bc = new BestCom();
Windows w = new Windows();
Fluctuate f = new Fluctuate();
// 表2中的重量段、单数和重量
//原始数据
List<WeightSegment> weightSegments = Arrays.asList(
new WeightSegment("0.00-0.1kg", 69700, 2353.91),
new WeightSegment("0.11-0.2kg", 57481, 8182.675),
new WeightSegment("0.21-0.3kg", 56488, 14054.609),
new WeightSegment("0.31-0.4kg", 35921, 12922.021),
new WeightSegment("0.41-0.5kg", 49474, 22938.796),
new WeightSegment("0.51-0.6kg", 18838, 10469.116),
new WeightSegment("0.61-0.7kg", 36880, 25183.539),
new WeightSegment("0.71-0.8kg", 30888, 21976.825),
new WeightSegment("0.81-0.9kg", 17089, 14837.163),
new WeightSegment("0.91-1.0kg", 18751, 17985.321),
new WeightSegment("1.01-1.1kg", 16246, 17353.769),
new WeightSegment("1.11-1.2kg", 15747, 17659.223),
new WeightSegment("1.21-1.3kg", 9875, 12434.107),
new WeightSegment("1.31-1.4kg", 17037, 23326.637),
new WeightSegment("1.41-1.5kg", 7621, 11220.825),
new WeightSegment("1.51-1.6kg", 10285, 15793.204),
new WeightSegment("1.61-1.7kg", 8917, 14893.159),
new WeightSegment("1.71-1.8kg", 7230, 12616.81),
new WeightSegment("1.81-1.9kg", 11384, 21475.125),
new WeightSegment("1.91-2.0kg", 5552, 10930.969),
new WeightSegment("2.01-2.5kg", 27178, 61033.246),
new WeightSegment("2.51-3.0kg", 19794, 50752.39)
);
// 波动方向和幅度
Scanner scanner = new Scanner(System.in);
System.out.println("请输入波动方向(小重量/平均重量/大重量):");
String direction = scanner.nextLine();
System.out.println("请输入波动幅度(百分比,如10):");
double fluctuationPercentage = scanner.nextDouble();
//先计算一次最好的公司,生成一次表格
int[] data1=bc.countBestCompanies(weightSegments);
w.showUI(data1);
//再进行数据波动,再一次生成最好的公司,生成表格
List<WeightSegment> nSegment =f.fluctuateData(weightSegments,direction,fluctuationPercentage);
int[] data2= bc.countBestCompanies(nSegment);
w.showUI(data2);
// 保持程序运行,直到用户关闭窗口
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
除了调用其他两个类之外,最后还加上了一个死循环,保证窗体不会因为程序结束而直接关闭
整体效果示例
(向平均重量浮动,幅度为20%,左图为浮动之前的图表,右图为浮动之后的图表)