今天接到老大給的一個任務,讓我作一個從一些流量中,按照模版進行採樣。須要按照等比例和均分。java
例如: 模版有A和B,總數量是10個,A有4個,B有6個。 假設如今須要採5個: 若是按照等比例分配:那麼A要採2個,B要才3個。 假設如今須要採6個: 按照均分,A和B個才3個。
理想狀況下,若是都是上面的這種固然好了,可以整除。可是不少狀況下是不能整除的,可是也要保證達到採樣的總數。測試
要求:
每一個模版都要採到。code
廢話很少說,直接上代碼。blog
/*** * 等比例採樣 * @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); }
注意:
這裏當採樣數量小於模版數量的時候,異常處理我這邊省略了。
當採樣數量大於總數的時候,不須要作任何處理,所有采。這裏面我也省略了。排序
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
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
把sampleTotal設置成14;
im
當咱們測試了兩個零界點以後,是沒有問題的,那麼中間的數量就沒什麼問題了。
把sampleTotal設置成9;