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;

相關文章
相關標籤/搜索