基于性别与班级属性的学生最大多样性分组方案咨询
最大化学生分组多样性的实现方案
现有20名学生,需划分为6个3人组和1个2人组,核心目标是最大化每组内性别与班级的多样性——即尽量减少组内同性别或同班级学生的占比,促进不同背景学生融合。此前尝试用KMeans聚类仅完成了属性数值化,未解决分组分配问题,以下是可行的实现方案。
一、先明确学生属性分布
首先统计原始数据中各属性组合的学生数量,这是分组的基础:
import pandas as pd data = [{'name': 'Katina', 'gender': 'f', 'class': 'a'}, {'name': 'Mary', 'gender': 'f', 'class': 'a'}, {'name': 'Manuel', 'gender': 'm', 'class': 'b'}, {'name': 'Timothy', 'gender': 'm', 'class': 'b'}, {'name': 'Eunice', 'gender': 'f', 'class': 'a'}, {'name': 'Gary', 'gender': 'm', 'class': 'a'}, {'name': 'Michael', 'gender': 'm', 'class': 'a'}, {'name': 'Melanie', 'gender': 'f', 'class': 'b'}, {'name': 'Edward', 'gender': 'm', 'class': 'a'}, {'name': 'Jessie', 'gender': 'm', 'class': 'b'}, {'name': 'Elizabeth', 'gender': 'f', 'class': 'a'}, {'name': 'Jared', 'gender': 'm', 'class': 'a'}, {'name': 'Jose', 'gender': 'm', 'class': 'b'}, {'name': 'Amado', 'gender': 'm', 'class': 'b'}, {'name': 'Matthew', 'gender': 'm', 'class': 'a'}, {'name': 'Charlie', 'gender': 'm', 'class': 'a'}, {'name': 'Willie', 'gender': 'm', 'class': 'b'}, {'name': 'Mary', 'gender': 'f', 'class': 'a'}, {'name': 'Susan', 'gender': 'f', 'class': 'b'}, {'name': 'Michael', 'gender': 'm', 'class': 'b'}] df = pd.DataFrame(data) # 统计各属性组合的人数 group_counts = df.groupby(['gender', 'class']).size().reset_index(name='count') print(group_counts)
运行后得到各群体人数:
| gender | class | count |
|---|---|---|
| f | a | 6 |
| f | b | 3 |
| m | a | 7 |
| m | b | 4 |
二、为什么KMeans不适用
KMeans的核心逻辑是将相似样本聚集到同一簇,但我们的需求是异质分组——让每组包含尽可能多不同属性的学生,两者目标完全相反,因此KMeans无法直接解决这个问题。
三、可行方案:贪心算法(简单易实现)
贪心算法的思路是:每次组建组时,优先从当前剩余人数最多的属性组合中选取学生,再搭配其他属性组合的学生,尽量让每组覆盖最多的不同类别,避免同属性扎堆。
实现代码
import random # 按属性组合拆分学生列表 fa_students = df[(df['gender'] == 'f') & (df['class'] == 'a')]['name'].tolist() fb_students = df[(df['gender'] == 'f') & (df['class'] == 'b')]['name'].tolist() ma_students = df[(df['gender'] == 'm') & (df['class'] == 'a')]['name'].tolist() mb_students = df[(df['gender'] == 'm') & (df['class'] == 'b')]['name'].tolist() # 打乱列表,增加随机性 random.shuffle(fa_students) random.shuffle(fb_students) random.shuffle(ma_students) random.shuffle(mb_students) groups = [] # 先创建6个3人组 for _ in range(6): group = [] # 按剩余人数降序排序候选群体 candidates = [('fa', fa_students), ('fb', fb_students), ('ma', ma_students), ('mb', mb_students)] candidates.sort(key=lambda x: len(x[1]), reverse=True) # 优先选不同属性组合的学生 for i in range(3): if len(candidates[i][1]) > 0: group.append(candidates[i][1].pop()) else: # 若当前类别为空,从下一个非空类别补选 for j in range(i+1, 4): if len(candidates[j][1]) > 0: group.append(candidates[j][1].pop()) break groups.append(group) # 创建最后1个2人组 last_group = [] candidates = [('fa', fa_students), ('fb', fb_students), ('ma', ma_students), ('mb', mb_students)] candidates.sort(key=lambda x: len(x[1]), reverse=True) # 优先选不同属性组合的学生 for i in range(2): if len(candidates[i][1]) > 0: last_group.append(candidates[i][1].pop()) groups.append(last_group) # 输出分组结果 for idx, group in enumerate(groups, 1): print(f"组{idx}: {group}")
效果说明
该算法会尽量让每个3人组包含2-3种不同的属性组合,2人组包含2种不同组合,最大化组内多样性。由于加入了随机打乱,每次运行结果会有差异,但都能满足多样性要求。
四、进阶方案:整数规划(最优解)
如果需要严格的最优解(即所有组的多样性总和最大),可以用整数规划工具(如PuLP),定义目标函数为最小化所有组内同性别或同班级的学生对数,约束条件包括:
- 每个学生仅属于一个组
- 6个组有3名学生,1个组有2名学生
核心思路示例
from pulp import LpProblem, LpMinimize, LpVariable, lpSum # 定义基础数据 students = df['name'].tolist() num_students = len(students) groups = [f'group_{i}' for i in range(7)] group_sizes = [3]*6 + [2] # 初始化规划问题 prob = LpProblem("MaximizeGroupDiversity", LpMinimize) # 定义变量:x[i][g] = 1表示学生i属于组g x = LpVariable.dicts("x", [(s, g) for s in students for g in groups], cat='Binary') # 目标函数:最小化组内同性别+同班级的学生对数 cost = 0 for g in groups: # 计算同性别对数 for i in range(num_students): for j in range(i+1, num_students): if df.loc[df['name'] == students[i], 'gender'].values[0] == df.loc[df['name'] == students[j], 'gender'].values[0]: cost += x[(students[i], g)] * x[(students[j], g)] # 计算同班级对数 for i in range(num_students): for j in range(i+1, num_students): if df.loc[df['name'] == students[i], 'class'].values[0] == df.loc[df['name'] == students[j], 'class'].values[0]: cost += x[(students[i], g)] * x[(students[j], g)] prob += cost # 约束1:每个学生仅在一个组 for s in students: prob += lpSum([x[(s, g)] for g in groups]) == 1 # 约束2:每组人数符合要求 for g_idx, g in enumerate(groups): prob += lpSum([x[(s, g)] for s in students]) == group_sizes[g_idx] # 求解 prob.solve() # 输出分组结果 group_assignments = {g: [] for g in groups} for s in students: for g in groups: if x[(s, g)].value() == 1: group_assignments[g].append(s) for g, members in group_assignments.items(): print(f"{g}: {members}")
说明
整数规划能给出理论上的最优解,但实现复杂度更高,适合对分组结果要求严格的场景。
内容的提问来源于stack exchange,提问作者RJ Adriaansen
相关产品推荐
相关产品推荐

