基于饮食偏好的3组人群分配算法实现问题,适配任意参与人数
人员分组算法实现方案
前置规则梳理
- 人员饮食类型共4类:meat(仅可去A组)、vegan(仅可去B组)、vegetarian(优先去C组,C放不下再去B组)、no_food_preference(可去任意组,用于补全各组6人倍数的缺口)
- 各组准入要求:
- Group A:仅允许meat、no_food_preference进入
- Group B:仅允许vegan、vegetarian、no_food_preference进入
- Group C:仅允许vegetarian、no_food_preference进入
- 最终要求:每个组总人数必须是6的整数倍
算法执行步骤
- 人员池拆分:将所有人员按饮食偏好拆分为4个独立池,分别统计各池人数
- 强制归属分配:将只能进入单个组的meat全部划入A组,vegan全部划入B组
- 预留A组补员:A组仅能通过无偏好人员补全到6的倍数,先从无偏好池中扣除A组所需的最少补员数量,完成A组第一次人数凑整
- 分配vegetarian群体:优先将vegetarian划入C组,匹配无偏好池剩余人数将C组总人数凑整到6的倍数,无法放入C组的vegetarian全部划入B组
- B组人数凑整:用剩余无偏好人员将B组总人数凑整到6的倍数
- 剩余无偏好人员处理:此时剩余无偏好人员数量一定是6的整数倍,可按6人一组批量加入任意符合准入规则的组即可
代码实现(针对示例数据)
import pandas as pd # 加载示例数据 df = pd.DataFrame( { "user_id": [i for i in range(1, 55)], "Master_FoodPreference": ["meat", "vegetarian", "meat", "vegan", "meat", "vegetarian", "meat", "vegetarian", "no_food_preference", "meat",'no_food_preference', 'vegetarian',"meat", "meat", "vegetarian", "vegetarian", "vegan", "vegetarian", "vegetarian", "no_food_preference", "vegan", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "meat", "vegetarian", "meat", "vegetarian", "no_food_preference", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "vegetarian", "no_food_preference", "no_food_preference", "no_food_preference", "meat", "no_food_preference", "meat", "meat", "vegan", "no_food_preference", "no_food_preference", "vegan" ,"no_food_preference" ,"vegan" ,"vegan" ] } ) # 步骤1:拆分四个人员池 pool_meat = df[df['Master_FoodPreference'] == 'meat']['user_id'].tolist() pool_vegan = df[df['Master_FoodPreference'] == 'vegan']['user_id'].tolist() pool_vegetarian = df[df['Master_FoodPreference'] == 'vegetarian']['user_id'].tolist() pool_no_pref = df[df['Master_FoodPreference'] == 'no_food_preference']['user_id'].tolist() # 初始化各组 group_a = [] group_b = [] group_c = [] # 步骤2:强制分配只能去单组的人员 group_a.extend(pool_meat) group_b.extend(pool_vegan) # 步骤3:补全A组到6的倍数 need_a = (6 - len(group_a) % 6) % 6 group_a.extend(pool_no_pref[:need_a]) pool_no_pref = pool_no_pref[need_a:] # 步骤4:优先分配vegetarian到C组并凑整 vt_count = len(pool_vegetarian) need_c = (6 - vt_count % 6) % 6 if need_c <= len(pool_no_pref): # 全部vegetarian都可放入C组 group_c.extend(pool_vegetarian) group_c.extend(pool_no_pref[:need_c]) pool_no_pref = pool_no_pref[need_c:] remaining_vt = [] else: # 容量不足,调整C组可容纳的vegetarian数量 available_total = vt_count + len(pool_no_pref) max_vt_in_c = vt_count - (available_total % 6) group_c.extend(pool_vegetarian[:max_vt_in_c]) group_c.extend(pool_no_pref) pool_no_pref = [] remaining_vt = pool_vegetarian[max_vt_in_c:] # 剩余vegetarian放入B组 group_b.extend(remaining_vt) # 步骤5:补全B组到6的倍数 need_b = (6 - len(group_b) %6) %6 group_b.extend(pool_no_pref[:need_b]) pool_no_pref = pool_no_pref[need_b:] # 步骤6:剩余无偏好人员(一定是6的倍数)批量加入A组(也可加入B/C,均符合规则) group_a.extend(pool_no_pref) # 结果验证与输出 print(f"Group A总人数:{len(group_a)},是否为6的倍数:{len(group_a)%6==0}") print(f"Group B总人数:{len(group_b)},是否为6的倍数:{len(group_b)%6==0}") print(f"Group C总人数:{len(group_c)},是否为6的倍数:{len(group_c)%6==0}")
示例运行结果
针对给出的54人示例数据运行代码后,输出如下:
- Group A总人数18,人员为全部meat类人员+6名无偏好人员,符合准入规则
- Group B总人数12,人员为全部vegan类人员+5名无偏好人员,符合准入规则
- Group C总人数24,人员为全部vegetarian类人员,符合准入规则
所有组人数均为6的整数倍,完全满足需求。
内容的提问来源于stack exchange,提问作者PParker
相关产品推荐
相关产品推荐

