有初步构想了,但是具体下来总是写不出来,求完善全部编码

img

img

请把你的初步构想写清楚

时间有点久了哈,按题主的解题思路,每个学生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) 

最终结果:

img

执行次数: 33457364
最终方差: 0.12244897959183676
####可能还有其他组合办法,本次不予考虑。

后续优化方向:
①:弄清楚最优排序肯定是6个5项,1个6项之后,在第一步的基础上对全体数据进行筛选
剔除存在单一老师出席次数脱离[5,6]区间的排列。降低整体运行时间
②:多层循环看上去不大美观,如何优美的循环每一种排列组合呢?
③:变量过多,部分变量未使用。