• 【工作中问题解决实践 一】最小单元染色法的应用


    最近在处理费率和保底费的优惠及标准区间。问题本质就是:标准合作区间是一个【时间段+标准值】,优惠合作区间是多段【时间段+标准值】,并且各个时间段的开始和结束日期可以随意指定,优惠区间和标准区间重合部分按照优惠值计算,非重合部分按照标准区间值计算

    问题场景

    上游对时间段的限制基本没有,唯一的限制就是多段优惠区间是连续的。
    在这里插入图片描述
    优惠期结束晚于合作期
    在这里插入图片描述优惠期开始早于合作期
    在这里插入图片描述
    优惠期大于合作期
    在这里插入图片描述
    优惠期和合作期完全不沾边
    在这里插入图片描述
    由于各种可能行都存在,用if else条件判断起来太麻烦,可能性太多了

    解决方法

    当问题复杂的时候不妨把问题抽象看待:由于费率和保底费的计算都是以天为最小单位的,所以我们可以把每段时间都拆成一天一天的,放到一个集合里,然后以标准的时间值Map为基准,匹配优惠的,匹配到就修改该最小单元对应的值为优惠值,匹配不到则继续匹配,直到匹配结束。匹配完成后我们再依据值的连续性,将连续相同值的最小单元输出为时间段和值

    1 最小单元拆分

    第一步:分别将连续的优惠区间和标准区间进行最小单元化处理:

    /**
         * 将时间段和值拆分为天和值
         *
         * @param beginDate the begin date
         * @param endDate   the end date
         * @param fee       the fee
         * @return the tree map
         */
        public TreeMap<LocalDate, BigDecimal> Build(LocalDate beginDate, LocalDate endDate, BigDecimal fee) {
            TreeMap<LocalDate, BigDecimal> feeMap = new TreeMap<>();
            for (LocalDate date = beginDate; date.isBefore(endDate) || date.isEqual(endDate); date = date.plusDays(1)) {
                date = DateUtil.stringToLocalDate(date.format(fmt), DateUtil.DATE_FORMAT);
                feeMap.put(date, fee);
            }
            return feeMap;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2 最小单元染色

    第二步:对最小单元区间进行Merge处理:基于标准区间,对重合部分的优惠区间重新赋值

    /**
         * 以标准值时间段为基准,相同时间天的按优惠值
         *
         * @param standard the standard
         * @param discount the discount
         */
        public void Merge(TreeMap<LocalDate, BigDecimal> standard, TreeMap<LocalDate, BigDecimal> discount) {
            Set<LocalDate> days = standard.keySet();
            for (LocalDate day : days) {
                if (discount.containsKey(day)) {
                    standard.put(day, discount.get(day));
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3 最小单元区间合并

    第三步:将处理好的最小单元和值Map进行连续值时间区间合并

    /**
         * 将最小单元再转为时间段和值
         *
         * @param full the full
         * @return the list
         */
        public List<DateAndValueInfo> Output(TreeMap<LocalDate, BigDecimal> full) {
            List<DateAndValueInfo> dateAndValueInfos = new ArrayList<>();
            Set<LocalDate> days = full.keySet();
            BigDecimal fee = new BigDecimal("0");
            LocalDate start = null;
            LocalDate end = null;
            int count = 0;
            for (LocalDate day : days) {
                count++;
                if (fee.toPlainString().equals(ZERO) && start == null) {
                    fee = full.get(day);
                    start = day;
                    end = day;
                    continue;
                }
                if (!fee.equals(full.get(day))) {
                    // 相同值连续阶段结束时,将本段连续值添加到时间段,最后一阶段也添加
                    Date beforeStartDate = DateUtil.localDateToDate(start);
                    Date beforeEndDate = DateUtil.localDateToDate(end);
                    DateAndValueInfo dateAndValueInfo = new DateAndValueInfo(beforeStartDate, beforeEndDate, fee);
                    dateAndValueInfos.add(dateAndValueInfo);
                    start = day;
                    fee = full.get(day);
                }
                end = day;
                if (count == days.size()) {
                    // 最后一阶段也添加
                    Date beforeStartDate = DateUtil.localDateToDate(start);
                    Date beforeEndDate = DateUtil.localDateToDate(end);
                    DateAndValueInfo dateAndValueInfo = new DateAndValueInfo(beforeStartDate, beforeEndDate, fee);
                    dateAndValueInfos.add(dateAndValueInfo);
                }
            }
    
            return dateAndValueInfos;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    整体的代码调用如下:

    /**
         * 合并处理时间段和值
         *
         * @param disCountDateAndValueInfo the dis count date and value info
         * @param standardDateAndValueInfo the standard date and value info
         * @return the list
         */
        public List<DateAndValueInfo> handleDateAndValues(List<DateAndValueInfo> disCountDateAndValueInfo, DateAndValueInfo standardDateAndValueInfo) {
            TreeMap<LocalDate, BigDecimal> disCounts = new TreeMap<>();
            for (DateAndValueInfo dateAndValueInfo : disCountDateAndValueInfo) {
                TreeMap<LocalDate, BigDecimal> disCount = Build(DateUtil.dateToLocalDate(dateAndValueInfo.getFromDate()), DateUtil.dateToLocalDate(dateAndValueInfo.getToDate()), dateAndValueInfo.getValue());
                disCounts.putAll(disCount);
            }
            TreeMap<LocalDate, BigDecimal> standard = Build(DateUtil.dateToLocalDate(standardDateAndValueInfo.getFromDate()), DateUtil.dateToLocalDate(standardDateAndValueInfo.getToDate()), standardDateAndValueInfo.getValue());
            Merge(standard, disCounts);
            return Output(standard);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    用最小单元染色法解决甚至可以处理这样毫无规律的时间段merge
    在这里插入图片描述

    拓展处理

    还可以解决这类问题:当不需要处理标准段而只需要用标准区间卡一个时间范围的话:
    在这里插入图片描述

    /**
         * 获取优惠段和合作区间merge的时间
         *
         * @param disCountDateAndValueInfo the dis count date and value info
         * @param standardDateAndValueInfo the standard date and value info
         * @return the list
         */
        private List<DateAndValueInfo> handleValidateInfo(List<DateAndValueInfo> disCountDateAndValueInfo, DateAndValueInfo standardDateAndValueInfo) {
            // 所有优惠期统一划分为细粒度值-时间
            TreeMap<LocalDate, BigDecimal> disCountInfos = new TreeMap<>();
            for (DateAndValueInfo dateAndValueInfo : disCountDateAndValueInfo) {
                TreeMap<LocalDate, BigDecimal> disCount = Build(DateUtil.dateToLocalDate(dateAndValueInfo.getFromDate()), DateUtil.dateToLocalDate(dateAndValueInfo.getToDate()), dateAndValueInfo.getValue());
                disCountInfos.putAll(disCount);
            }
            LocalDate realStartDate = disCountInfos.firstKey().isBefore(DateUtil.dateToLocalDate(standardDateAndValueInfo.getFromDate())) ? DateUtil.dateToLocalDate(standardDateAndValueInfo.getFromDate()) : disCountInfos.firstKey();
            LocalDate realEndDate = disCountInfos.lastKey().isBefore(DateUtil.dateToLocalDate(standardDateAndValueInfo.getToDate())) ? DateUtil.dateToLocalDate(standardDateAndValueInfo.getToDate()) : disCountInfos.lastKey();
            // 不在卡死时间段内的值干掉
            Set<LocalDate> days = disCountInfos.keySet();
            for (LocalDate day : days) {
                if (day.isBefore(realStartDate) || day.isAfter(realEndDate)) {
                    disCountInfos.remove(day);
                }
            }
    
            return Output(disCountInfos);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    总结一下

    大道至简,当问题复杂时,一定是因为思维陷入到了复杂的逻辑里,要学会思考如何抽象看待问题,透过现象看本质。

  • 相关阅读:
    功率放大器应用领域分享:微流控细胞分选在“软骨”芯片关节炎治疗研究中的应用
    linux常用基本命令实例(常用的linux命令建议收藏)
    【LeetCode】63. 不同路径 II
    14-RPC-自研微服务框架
    hot100-数组中的第k个最大元素
    视频讲解|含可再生能源的热电联供型微网经济运行优化(含确定性和源荷随机两部分代码)
    java毕业设计疫情网课管理系统Mybatis+系统+数据库+调试部署
    百趣代谢组学资讯:慢性肾病的延缓方法有了!
    AK 9.12 百度Java后端研发B卷 笔试
    Web前端:一些优化React Native应用程序性能的有用技巧
  • 原文地址:https://blog.csdn.net/sinat_33087001/article/details/126305020