JAVA计算饼图百分比合计不是100的问题处理,最大余额法

背景

由于各种原因需要后端来计算每个项所占的百分比,但是会发现计算的各项百分比合计不绝对是100,不能简单的对各项使用四舍五入法,舍九法,进一法等。

思路

使用最大余额法(比例代表制投票制度下,一种议席分配的方法,结尾有描述)

举个具体例子就很好理解:总计29个员工给3个组投票,请计算出各组所占的百分比,精确到整数。

常规计算出百分比如下:

组名 票数 计算值(%) 精确到整数 小数余额
A组 10 34.482..... 34 0.482.....
B组 12 41.379..... 41 0.379.....
C组 7 24.137..... 24 0.137.....

但是很明显,计算值存在小数,产品经理要求精确到整数,且最终的百分比合计又需要等于100,还剩下最后一个数分给谁?

按照最大余额法,应该将最后一个数分给小数余额最大的A组(0.482.....)。同理,如果多出两个数,第二多出的数应该分给小数余额第二大的组。

使用java编码实现如下

为了方便观察,封装了实体类:

@Data
@NoArgsConstructor
public class PercentVo {
    /**
     * 数量
     */
    private int number;
    /**
     * 百分比
     */
    private int percent;
    /**
     * 小数点后的余额,不需要返回给前端
     */
    @JSONField(serialize = false)
    private Double point;
    /**
     * 组名
     */
    private String name;

    public PercentVo(int number, String name) {
        this.number = number;
        this.name = name;
    }
}

方法及测试类:

public class NumberUtils {

    /**
     * 最大余额法,精度为整数
     *
     * @param list
     */
    public static void calculatePercent(List list) {
        //求和:总票数
        int sum = list.stream().mapToInt(PercentVo::getNumber).sum();
        for (PercentVo percentVo : list) {
            double value = percentVo.getNumber() * 100 / (double) sum;
            int valueInt = (int) value;
            //设置对应的百分比
            percentVo.setPercent(valueInt);
            //获取小数点后的值
            percentVo.setPoint(value - valueInt);
            System.out.println("percentVo = " + percentVo);
        }
        //求和:当前各项百分比合计。由于我们舍弃了小数位,所以该合计只会小于等于100
        int curSum = list.stream().mapToInt(PercentVo::getPercent).sum();
        while (curSum < 100) {
            //找出小数余额最大的组,对其进行加1
            PercentVo max = list.stream().max(Comparator.comparingDouble(PercentVo::getPoint)).get();
            max.setPercent(max.getPercent() + 1);
            //当前这个数已经加1了,不应该参与下一轮的竞选
            max.setPoint(0.0);
            curSum++;
        }
    }

    public static void main(String[] args) {
        PercentVo v1 = new PercentVo(10, "A组");
        PercentVo v2 = new PercentVo(12, "B组");
        PercentVo v3 = new PercentVo(7, "C组");
        List list = Arrays.asList(v1, v2, v3);
        System.out.println("处理前--------------------------");
        calculatePercent(list);
        System.out.println("处理后--------------------------");
        for (PercentVo percentVo : list) {
            System.out.println(percentVo);
        }
    }
}

结果打印如下:

处理前--------------------------
percentVo = PercentVo(number=10, percent=34, point=0.48275862068965836, name=A组)
percentVo = PercentVo(number=12, percent=41, point=0.3793103448275872, name=B组)
percentVo = PercentVo(number=7, percent=24, point=0.137931034482758, name=C组)
处理后--------------------------
PercentVo(number=10, percent=35, point=0.0, name=A组)
PercentVo(number=12, percent=41, point=0.3793103448275872, name=B组)
PercentVo(number=7, percent=24, point=0.137931034482758, name=C组)

扩展:这里为了方便,仅做了精确到整数,还可以扩展为:添加入参2,自定义精度。来提高适用性,思路是一样的。

延申

最大余额方法(英文:Largest Remainder Method)又称数额制,是比例代表制投票制度下,一种议席分配的方法,相对于最高均数方法。

透过最大余额方法,候选人须以名单参选,每份名单的人数最多可达至相关选区内的议席数目。候选人在名单内按优先次序排列。选民投票给一份名单,而不是个别候选人。投票结束后,把有效选票除以数额。一份名单每取得数额1倍的票数,便能获分配一个议席。每份名单的候选人按原先订立的顺序当选。

如此类推、将议席分配至每份名单的余额,均比数额为低的时候,则从最大余额者顺序分配余下议席;最大余额方法因而得名。

你可能感兴趣的:(java,java,算法)