基于年龄与性别将n人划分为m个均衡子组及实现shuffle功能的算法咨询
均衡分组与保持均衡的随机置换算法方案
前置合法性校验
分组前先做基础校验,判断当前人员池是否支持符合要求的划分:
- 将所有人按「出生年份+性别」做独立分层,统计每个分层的总人数
- 若要求所有子组必须包含全部年份+性别组合,需保证每个分层的总人数≥m;若允许部分子组缺少个别分层(如示例中1982年男性仅2人,第3组无该分层人员),则无强制要求,最终各子组同一分层的人数差不会超过1。
核心分组算法:分层随机均摊法
该算法完全匹配你的需求,逻辑简单且性能高:
- 对每个「出生年份+性别」的独立分层,先将该分层下的人员列表做随机打乱
- 按顺序把打乱后的列表循环均摊分配到m个组中:比如某分层共6人分3组,每组分配2人;某分层共2人分3组,前2组各分配1人,第3组分配0人。
该方式天然保证每个子组内同出生年份人数一致、同年龄层男女比例完全相同,你的示例输出完全符合该算法的分配结果。
保持均衡的随机置换函数实现
要实现随机调换人员且不破坏分组均衡,核心规则是仅能在同一个「出生年份+性别」分层内做跨组交换,绝对不允许跨分层交换人员,实现步骤如下:
- 提前为每个分层维护「各组成员列表」的映射关系
- 每次置换时,随机选择1个分层,再随机选择2个拥有该分层人员的不同子组,从两个子组的该分层人员中各随机选1人做交换
- 批量置换时重复上述操作指定次数即可,无论交换多少次都不会破坏各分层的人数分布,自然不会打破子组的年份人数、男女比例均衡。
参考伪代码
from collections import defaultdict import random # 均衡分组函数 def split_to_balanced_groups(people, group_num): # 按出生年份+性别聚合分层 layer_map = defaultdict(list) for person in people: layer_key = (person["birth_year"], person["gender"]) layer_map[layer_key].append(person) # 初始化空分组 groups = [[] for _ in range(group_num)] for layer_key, person_list in layer_map.items(): # 分层内先随机打乱 random.shuffle(person_list) # 循环均摊到各个组 for idx, person in enumerate(person_list): groups[idx % group_num].append(person) return groups, layer_map # 保持均衡的shuffle函数 def balanced_shuffle(groups, layer_map, swap_times=100): group_num = len(groups) # 构建分层的组内成员索引 layer_group_index = defaultdict(lambda: [[] for _ in range(group_num)]) for g_idx, group in enumerate(groups): for person in group: layer_key = (person["birth_year"], person["gender"]) layer_group_index[layer_key][g_idx].append(person) # 执行指定次数交换 for _ in range(swap_times): # 随机选一个有至少2个组存在成员的分层 layer_key = random.choice(list(layer_group_index.keys())) group_member_lists = layer_group_index[layer_key] valid_group_idx = [i for i in range(group_num) if len(group_member_lists[i]) > 0] if len(valid_group_idx) < 2: continue # 选两个不同的组,各挑1人交换 g1, g2 = random.sample(valid_group_idx, 2) p1 = random.choice(group_member_lists[g1]) p2 = random.choice(group_member_lists[g2]) # 更新分组列表 groups[g1].remove(p1) groups[g1].append(p2) groups[g2].remove(p2) groups[g2].append(p1) # 更新分层索引 group_member_lists[g1].remove(p1) group_member_lists[g1].append(p2) group_member_lists[g2].remove(p2) group_member_lists[g2].append(p1) return groups
内容的提问来源于stack exchange,提问作者user7913
相关产品推荐
相关产品推荐

