简单数学建模设计--电商公司快递智能分配策略优化研究(学校比赛)

题目如下

(发送该博客时间已经超过比赛时间,若有违反协议,请私信我删除)

某电商公司为降低物流成本并提升配送效率,需优化其快递分配策略,以求达到快递配送费用最省。公司根据包裹重量将订单划分为若干区间(如0-0.1kg、0.11-0.2kg等)(见表2),每个区间只能选择一家快递公司承运。不同快递公司在各重量区间的报价(见表1)。此外,市场环境动态变化(如运费调整、订单量波动、重量分布变化),需建立灵活的调整机制。请根据表1、表2数据及相关背景,完成下面2个问题:

  1. 请给出优化算法模型和流程图,并给出各重量段最优快递商分配策略;
  2. 随着市场环境的变化(如运费调整、订单量波动、重量分布变化),提出相应的调整策略(比如:当市场波动超过阈值(如±5%)时,提出再优化触发机制),并作灵敏度分析。

1:备选快递费用

快递

费用

广东极兔

总均重0.8kg 2.2元,每增加0.1kg0.16

广东韵达

总均重0.6kg 2.2元,每增加0.1kg0.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%,左图为浮动之前的图表,右图为浮动之后的图表)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值