Java实现采样,等比例分和均分
1. 需求分析#
今天接到老大给的一个任务,让我做一个从一些流量中,按照模版进行采样。需要按照等比例和均分。
例如:
模版有A和B,总数量是10个,A有4个,B有6个。
假设现在需要采5个:
如果按照等比例分配:那么A要采2个,B要才3个。
假设现在需要采6个:
按照均分,A和B个才3个。
理想情况下,如果都是上面的这种当然好了,能够整除。但是很多情况下是不能整除的,但是也要保证达到采样的总数。
要求:
每个模版都要采到。
废话不多说,直接上代码。
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.2. 验证下不能整除的情况下。
这里面测试两个零界点。
3.2.1 一个是数量等于模版总数
3.2.2 一个是采样数量 = 总数 - 1
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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App