Java实现采样,等比例分和均分

1. 需求分析

今天接到老大给的一个任务,让我作一个从一些流量中,按照模版进行采样。须要按照等比例和均分。java

例如:
模版有A和B,总数量是10个,A有4个,B有6个。

假设如今须要采5个:
若是按照等比例分配:那么A要采2个,B要才3个。

假设如今须要采6个:
按照均分,A和B个才3个。

理想状况下,若是都是上面的这种固然好了,可以整除。可是不少状况下是不能整除的,可是也要保证达到采样的总数。测试

要求:
每一个模版都要采到。code

废话很少说,直接上代码。blog

2. 相关代码

/***
     * 等比例采样
     * @param map   存放数据,须要按照数量正序排
     * @param total  总数量
     * @param sampleTotal 须要采样的数量
     */
    public static void allocateFlowByPercentage(Map<String,Integer> map, Integer total, Integer sampleTotal) {
        int newTotal = 0;
        int addCount = 0;
        int i = 0;
        double basePercentage = sampleTotal / total.doubleValue();

        Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> next = iterator.next();
            String key = next.getKey();
            if (sampleTotal == map.size()) {
                // 每一个模版分1个
                map.put(key, 1);
                System.out.println("模版" + key + ":原来有流量:" + next.getValue() + "个,采样:1个");
                newTotal++;
                continue;
            }
            double doubleCount = basePercentage * next.getValue();
            int newCount = (int) Math.round(doubleCount);
            if (newCount == 0) {
                newCount = 1;
                addCount++;
            } else if (newCount > doubleCount && addCount > 0 && newCount > 1) {
                addCount--;
                newCount--;
            }

            if (i == map.size() - 1) {
                // 最后一个不计算了,直接拿总数减去以前的总数。须要保证,map中存储的数量,是按照正序从小到大排序的
                newCount = sampleTotal - newTotal;
            }
            System.out.println("模版" + key + ":原来有流量:" + next.getValue() + "个,采样:" + newCount + "个");
            map.put(key, newCount);
            newTotal += newCount;
            i++;
        }
        System.out.println("实际采样的总数:" + newTotal);
    }
/***
     * 均分采样
     * @param map   存放数据,须要按照数量正序排
     * @param sampleTotal 须要采样的数量
     */
    public static void allocateFlowByAverage(Map<String,Integer> map, Integer sampleTotal) {
        int newTotal = 0;
        int i = 0;
        double averageCount = sampleTotal.doubleValue() / map.size();

        Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> next = iterator.next();
            String key = next.getKey();
            if (sampleTotal == map.size()) {
                // 每一个模版分1个
                map.put(key, 1);
                System.out.println("模版" + key + ":原来有流量:" + next.getValue() + "个,采样:1个");
                newTotal++;
                continue;
            }
            int newCount = next.getValue();
            if (newCount > averageCount) {
                newCount = (int) Math.round(averageCount);
            }

            if (i == map.size() - 1) {
                // 最后一个不计算了,直接拿总数减去以前的总数。须要保证,map中存储的数量,是按照正序从小到大排序的
                newCount = sampleTotal - newTotal;
            }
            System.out.println("模版" + key + ":原来有流量:" + next.getValue() + "个,采样:" + newCount + "个");
            map.put(key, newCount);
            newTotal += newCount;
            i++;
        }
        System.out.println("实际采样的总数:" + newTotal);
    }

注意:
这里当采样数量小于模版数量的时候,异常处理我这边省略了。
当采样数量大于总数的时候,不须要作任何处理,所有采。这里面我也省略了。排序

3. 验证

public static void main(String[] args) {
        // 保证添加的顺序是从小到大
        Map<String,Integer> map = new LinkedHashMap<>();
        map.put("D", 4);
        map.put("E", 6);
        Integer total = 10;
        Integer sampleTotal = 5;
        System.out.println("========= 等比例采样 ===========");
        allocateFlowByPercentage(map, total, sampleTotal);
        System.out.println();
        System.out.println("========= 均分采样 ===========");
        
        map.put("D", 4);
        map.put("E", 6);
        sampleTotal = 6;
        allocateFlowByAverage(map, sampleTotal);
    }

3.1. 先来验证下能整除的状况下。
get

3.2. 验证下不能整除的状况下。it

这里面测试两个零界点。

3.2.1 一个是数量等于模版总数

3.2.2 一个是采样数量 = 总数 - 1class

3.3 数量等于模版总数

public static void main(String[] args) {
        // 保证添加的顺序是从小到大
        Map<String,Integer> map = new LinkedHashMap<>();
        map.put("A", 1);
        map.put("B", 1);
        map.put("C", 3);
        map.put("D", 4);
        map.put("E", 6);
        Integer total = 15;
        Integer sampleTotal = 5;
        System.out.println("========= 等比例采样 ===========");
        allocateFlowByPercentage(map, total, sampleTotal);
        System.out.println();
        System.out.println("========= 均分采样 ===========");

        map.put("A", 1);
        map.put("B", 1);
        map.put("C", 3);
        map.put("D", 4);
        map.put("E", 6);
        sampleTotal = 5;
        allocateFlowByAverage(map, sampleTotal);
    }

结果是:
map

3.4 采样数量 = 总数 - 1

把sampleTotal设置成14;
im

3.5 采样数量在 5 ~ 14之间

当咱们测试了两个零界点以后,是没有问题的,那么中间的数量就没什么问题了。
把sampleTotal设置成9;

相关文章
相关标签/搜索