请把你的初步构想写清楚
时间有点久了哈,按题主的解题思路,每个学生s1从P1-12中选一位,再从W1-12中选两位,
但是后续不管几层for循环,都是针对s1/s2..的。不能解决后续的问题。
并且如此枚举内存肯定顶不住哈哈哈。
解题思路 :
第一,如何找出所有满足规则的排列方式,以及如何存储?
第二,如何在海量排列方式中找到老师出席次数方差最小的一种/多种排列
第一步:
from itertools import combinations # Python自带函数,可以枚举所有情况
from typing import List
import numpy as np
import random
########导包
def org(): ###生成初始数据
list_sum = ['s1-T1', 's2-T1', 's3-t2', 's4-t3', 's5-T4', 's6-T4', 's7-T4', 's8-T5', 's9-T5', 's10-t6', 's11-t6',
's12-t7']
stu_list = []
t_list = []
T_list = []
at_list: List[str] = []
for i in list_sum:
stu_list.append(i.split('-')[0])
t_str = i.split('-')[1]
if 'T' in t_str:
T_list.append(t_str)
else:
t_list.append(t_str)
at_list.append(t_str)
return stu_list, T_list, t_list, at_list
def get_all_list(index) : ###数据处理,获取所有可能
at_list.append(stu_list[index]) ###添加首位学生至所有老师
all_list= list(combinations(list(set(at_list)),4)) ###去重后获取随机组合全排列
at_list.pop(-1) ####原老师列表删除末位学生
result_list=[] ##最终排列组合
for index_1 in range(len(all_list)) :
temp_list=all_list[index_1] ##实际排列
if stu_list[index] in temp_list : ###学生一定得在排列里面
if ((index not in [1,4] ) and (at_list[index] not in temp_list)) : ##当序号不为2,5时,只要求对应老师(序号对应)不在排列里面
for y in temp_list : ### 遍历临时排列
if 'T' in y : ### 当本次排列有正教授时
result_list.append(temp_list) ###结果排列添加
break ### 当次序号结束
elif ((index in [1,4]) and (at_list[2] not in temp_list) and (at_list[7] not in temp_list)) : ###当序号为2,5时,对应t2,T5不能在排列里)
for y in temp_list: ### 同上,可组合,偷懒了- -。
if 'T' in y:
result_list.append(temp_list)
break
pass
result_list=list(set(result_list)) ### 去重
return result_list ### 返回最终排列集合
此时我们就得到满足规则的所有排列组合
但并没有考虑是否有老师'摸鱼'(不参与)
那第二步必需得考虑这一点
在尝试过程中,第一次是试图把每种排序放在一个列表里两两比较,G(至少9 的12次方往上,内存溢出哈哈哈)
第二次是每次只计算一种组合,与最初组合的方差对比保留小的组合。
代码如下:
def count_var(org_result_list,at_list_1): ###对每个排列计算方差的方法,at_list_1为去重后的老师.
res=[]
for i in org_result_list :
for ex_t in i :
for all_t in at_list_1 :
if ex_t in all_t :
res.append(ex_t) ###老师截取出来放res中
res_1=[]
times=0
for i in at_list_1 :
res_1.append(res.count(i))
times+=1
if times !=7 :
return 'G' ###不为7时不进行计算,返回一个G用于判断
else :
return np.var(res_1) ####返回方差
def get_fn(len_list,org_var) : ### 最终还是层层循环比较
####需要注意的是,这里我们人为可以判断出最小方差组合为5、5、5、5、5、6,既0.122.。
####所以不需要等循环跑完(计算量太大了),等方差小于0.13时即可退出程序,减少计算次数
fn_var=org_var
fn_times=0
fn_list=[]
for a in len_list[0] :
for b in len_list[1] :
for c in len_list[2] :
for d in len_list[3] :
for e in len_list[4] :
for f in len_list[5] :
for g in len_list[6] :
for h in len_list[7] :
for i in len_list[8] :
for j in len_list[9] :
for k in len_list[10] :
for l in len_list[11] :
if fn_var < 0.13 :
return fn_var,fn_list,fn_times
else :
fn_times+=1
print('当前第{}次,方差为{}'.format(fn_times,fn_var))
org_list = [a, b, c, d, e, f, g, h, i, j, k, l]
org_result_list_temp = []
for index in range(len(dealed_list)):
org_result_list_temp.append(dealed_list[index][org_list[index]])
if count_var(org_result_list_temp,at_list_1) =='G' :
continue
else :
temp_var=count_var(org_result_list_temp,at_list_1)
if temp_var >= fn_var :
continue
else :
fn_var=temp_var
fn_list=org_result_list_temp
###主体程序:
if __name__ == '__main__':
stu_list, t_list, T_list, at_list = org() ###生成初始数据集
dealed_list=[]
for i in range(len(stu_list)):
dealed_list.append(get_all_list(i)) ##循环跑完,dealed_list即为第一步结果
len_list=[]
for i in range(len(dealed_list)):
len_temp=len(dealed_list[i])
len_list.append(list(range(len_temp)))
org_list=[0,0,0,0,0,0,0,0,0,0,0,0]
org_result_list=[]
for index in range(len(dealed_list)):
org_result_list.append(dealed_list[index][org_list[index]])
###计算每个老师出席次数及方差
at_list_1=list(set(at_list)) ### 去重获得所有老师
org_var=count_var(org_result_list,at_list_1)
fn_var,fn_list,fn_times=get_fn(len_list,org_var)
print(fn_times,fn_var)
最终结果:
执行次数: 33457364
最终方差: 0.12244897959183676
####可能还有其他组合办法,本次不予考虑。
后续优化方向:
①:弄清楚最优排序肯定是6个5项,1个6项之后,在第一步的基础上对全体数据进行筛选,
剔除存在单一老师出席次数脱离[5,6]区间的排列。降低整体运行时间
②:多层循环看上去不大美观,如何优美的循环每一种排列组合呢?
③:变量过多,部分变量未使用。