0
点赞
收藏
分享

微信扫一扫

基于 python 实现的中小学随机化分班算法(思路、实现、代码以及打包好的可执行文件)


文章目录

  • ​​0. 请勿转作商用!​​
  • ​​1. 代码运行情况​​
  • ​​2. 需要提供的excel格式​​
  • ​​3. 最终结果​​
  • ​​4. 算法实现​​
  • ​​5. 代码​​
  • ​​6. 下载地址​​

0. 请勿转作商用!

1. 代码运行情况

    *****************************************************

代码会自动分配所有学生到每个班,并自动生成每个班级的表格

1. 每个班级男女生数量尽可能平均
2. 每个班级每个分段人数尽可能相等
3. 每个班级之间的所有科目平均分尽可能相近
4. 允许对每个人预设班级

* 分班采用随机算法,每次运行会尝试 20 次计算挑选最小值,多次运算代码可能会得到不同的结果

*****************************************************

需要提供的excel表格内的列大致分为三段:


(姓名 性别) (语文 数学 英语 科学 总分) (预设班级)
1. 姓名、性别等信息 ; 2. 成绩 ; 3. 预设班级

*****************************************************

1. 姓名、性别等信息: 可以添加 '姓名' '学号' 等信息,这些信息不会影响结果
信息的顺序没有影响,但是此段内容最后一列必须是性别
'性别' 的值只能是 '男' 或者 '女'

2. 成绩:顺序无关,但是必须以总分结尾

3. 预设班级: 可选是否存在,需要预设分分班的人后面用数字标明需要分班到哪个班级即可
注意此列需要使用阿拉伯数字即 1,2,3,4...
且预设班级的数值应该是 [1,分班数量] 区间内的数字
不可以超过分班数量

*****************************************************

举例1:姓名 性别 语文 数学 英语 科学 总分 预设班级

举例2:学号 姓名 性别 数学 语文 英语 科学 总分 预设班级

举例3:姓名 学号 性别 语文 数学 科学 英语 总分

*****************************************************

此项目开源仅仅是为了交流学习,请自觉遵守法律以及道德规范,请勿将其用于商业用途!
有任何问题可以联系QQ470585226

---by jnxxhzz
杭州二中白马湖学校

2. 需要提供的excel格式

基于 python 实现的中小学随机化分班算法(思路、实现、代码以及打包好的可执行文件)_python


这里 列 为第一部分内容,列 为第二部分内容,列

  1. 其中第一部分内容必须以 "性别"列 结尾,前面的内容可以随意增加,顺序无关
  2. 其中第二部分内容必须跟在 “性别” 列之后,全部为每门课的成绩分数,以 "总分"列结尾,其中课程数量可以随意增加,顺序无关
  3. 其中第三部分内容为可选内容,若需要预设班级则添加此列,在需要预设班级的人后面标上班级即可,注意若需要分为个班,预设班级的数字范围应该是

3. 最终结果

最终会在代码目录下生成 文件,文件内即为最终方案,每个班级的名单以及全校的总情况

基于 python 实现的中小学随机化分班算法(思路、实现、代码以及打包好的可执行文件)_分班_08


基于 python 实现的中小学随机化分班算法(思路、实现、代码以及打包好的可执行文件)_python_09

4. 算法实现

代码整体采用的是随机化算法
主要步骤分

  1. 将所有人按分数排序
  2. 型分班将所有人分成对应需求的 个班级(即按照 的顺序分班)。同时将所有人打上分段标记(即 也就是排名前多少人分为一段,此处默认选择 超过
  3. 调整预设班级,若有人存在预设班级而本人不在对应班级,则跟对应班级同
  4. 调整每个班级的男女数量(四种情况分别配平,交换男女时保证只能交换同分数段的人)
  1. 男女都超过高平均值的两个班级互换
  2. 女生超过高平均值的班级和男生超过低平均值的班级互换 (男生班级人数会变成高平均值)
  3. 男生超过高平均值的班级和女超过低平均值的班级互换 (女班级人数会变成高平均值)
  4. 女生低于低平均值的班级和女生等于高平均值班级互换 (男生班级人数会变成低平均值)
  1. 随机调整,每次随机挑选两个班,分别枚举两个班的人,若两人
  1. 性别相同
  2. 没有预设班级
  3. 随机在中挑选一种,若能满足则交换
    按两个班级的每门课平均分差值是否变小决定是否交换

以上交换共执行 次,多次测试发现一般最多只能交换几十次,所以一千次一定能使得误差无法再变小,但是因为算法完全随机,所以在任何一次步骤不同都会导致不同的结果,所以考虑随机调整 次最终选择误差最小的那一次作为答案,当然

5. 代码

  1. 将所有人按分数排序

= Read_Ex()
all_students = r.read_excel()
all_students = sorted(all_students, key=itemgetter('总分'), reverse = True)
need_class = int(input("请输入分班数:"))

  1. 型分班将所有人分成对应需求的个班级(即按照的顺序分班)。同时将所有人打上分段标记(即也就是排名前多少人分为一段,此处默认选择超过

# 初始化每个班级
finall_class = []
every_class = []
for i in range(need_class):
temp_map = {'男':0,'女':0}
temp_list = []
finall_class.append(temp_list)
every_class.append(temp_map)

# 计算分段人数
every_level = (int)(20 / need_class) * need_class
if every_level < 20:
every_level = every_level + need_class

# 蛇形分班 & 标记分段
now_class_number = 0
flag = 1
level_numebr = 1
now_level_number = 0
now_every_level = every_level

boys_number = 0 # 记录男生数量
girls_number = 0 # 记录女生数量

every_level_two = 0

for i in range(len(all_students)):
# 记录男女生数量
if all_students[i]['性别'] == '男':
boys_number = boys_number + 1
every_class[now_class_number]['男'] = every_class[now_class_number]['男'] + 1
else:
girls_number = girls_number + 1
every_class[now_class_number]['女'] = every_class[now_class_number]['女'] + 1

# 标记分段
all_students[i]['分段'] = level_numebr
now_level_number = now_level_number + 1
if now_level_number >= now_every_level:
if i + 1< len(all_students) and all_students[i + 1]['总分'] == all_students[i]['总分']:
now_every_level += need_class
else:
now_level_number = 0
level_numebr = level_numebr + 1
every_level_two = every_level_two + 1
if every_level_two >= 2:
now_every_level = now_every_level + every_level
every_level_two = 0

# 分班
finall_class[now_class_number].append(all_students[i]);
now_class_number = now_class_number + flag
if now_class_number >= need_class or now_class_number < 0:
now_class_number = now_class_number - flag
flag = -flag

  1. 调整预设班级,若有人存在预设班级而本人不在对应班级,则跟对应班级同

# 调整预设班级
if book_key.count('预设班级') != 0:
for i in range(need_class):
for p1 in range(len(finall_class[i])):
go_class = finall_class[i][p1]['预设班级']
if go_class != '' and i != int(go_class) - 1:
go_class = int(go_class) - 1
for p2 in range(len(finall_class[go_class])):
if finall_class[i][p1]['性别'] == finall_class[go_class][p2]['性别']:
finall_class[i][p1], finall_class[go_class][p2] = finall_class[go_class][p2], finall_class[i][p1]
break

  1. 调整每个班级的男女数量(四种情况分别配平,交换男女时保证只能交换同分数段的人)

def change_sex():
# print(every_boys_number1," ", every_girls_number1)
# print(every_boys_number2," ", every_girls_number2)

# 对男女超过平均值的班级配平男女

# 1. 男女都超过高平均值的两个班级互换
for boys_id in range(need_class):
while every_class[boys_id]['男'] > every_boys_number2:
once_flag = 0
for girls_id in range(need_class):
if boys_id != girls_id and every_class[girls_id]['女'] > every_girls_number2:
# 在 boys_id 班和 girls_id 班中寻找 分段 相同的男女生交换
for boy in range(len(finall_class[boys_id])):
if finall_class[boys_id][boy]['性别'] == '男' \
and ((book_key.count('预设班级') != 0 and finall_class[boys_id][boy]['预设班级'] == '') or book_key.count('预设班级') == 0):
for girl in range(len(finall_class[girls_id])):
if finall_class[girls_id][girl]['性别'] == '女' \
and ((book_key.count('预设班级') != 0 and finall_class[girls_id][girl]['预设班级'] == '') or book_key.count('预设班级') == 0) \
and finall_class[boys_id][boy]['分段'] == finall_class[girls_id][girl]['分段']:
finall_class[boys_id][boy], finall_class[girls_id][girl] = finall_class[girls_id][girl],finall_class[boys_id][boy]
every_class[boys_id]['男'] = every_class[boys_id]['男'] - 1
every_class[boys_id]['女'] = every_class[boys_id]['女'] + 1
every_class[girls_id]['男'] = every_class[girls_id]['男'] + 1
every_class[girls_id]['女'] = every_class[girls_id]['女'] - 1
once_flag = 1;
break
if once_flag == 1:
break
if once_flag == 1:
break
if once_flag == 0:
break
# 2. 女生超过高平均值的班级和男生超过低平均值的班级互换 (男生班级人数会变成高平均值)
for girls_id in range(need_class):
while every_class[girls_id]['女'] > every_girls_number2:
once_flag = 0
for boys_id in range(need_class):
if boys_id != girls_id and every_class[boys_id]['男'] > every_boys_number1:
# 在 boys_id 班和 girls_id 班中寻找 分段 相同的男女生交换
for boy in range(len(finall_class[boys_id])):
if finall_class[boys_id][boy]['性别'] == '男'\
and ((book_key.count('预设班级') != 0 and finall_class[boys_id][boy]['预设班级'] == '') or book_key.count('预设班级') == 0):
for girl in range(len(finall_class[girls_id])):
if finall_class[girls_id][girl]['性别'] == '女' \
and ((book_key.count('预设班级') != 0 and finall_class[girls_id][girl]['预设班级'] == '') or book_key.count('预设班级') == 0) \
and finall_class[boys_id][boy]['分段'] == finall_class[girls_id][girl]['分段']:
finall_class[boys_id][boy], finall_class[girls_id][girl] = finall_class[girls_id][girl],finall_class[boys_id][boy]
every_class[boys_id]['男'] = every_class[boys_id]['男'] - 1
every_class[boys_id]['女'] = every_class[boys_id]['女'] + 1
every_class[girls_id]['男'] = every_class[girls_id]['男'] + 1
every_class[girls_id]['女'] = every_class[girls_id]['女'] - 1
once_flag = 1;
break
if once_flag == 1:
break
if once_flag == 1:
break
if once_flag == 0:
break
# 3. 男生超过高平均值的班级和女超过低平均值的班级互换 (女班级人数会变成高平均值)
for boys_id in range(need_class):
while every_class[boys_id]['男'] > every_boys_number2:
once_flag = 0
for girls_id in range(need_class):
if boys_id != girls_id and every_class[girls_id]['女'] > every_girls_number1:
# 在 boys_id 班和 girls_id 班中寻找 分段 相同的男女生交换
for boy in range(len(finall_class[boys_id])):
if finall_class[boys_id][boy]['性别'] == '男'\
and ((book_key.count('预设班级') != 0 and finall_class[boys_id][boy]['预设班级'] == '') or book_key.count('预设班级') == 0):
for girl in range(len(finall_class[girls_id])):
if finall_class[girls_id][girl]['性别'] == '女' \
and ((book_key.count('预设班级') != 0 and finall_class[girls_id][girl]['预设班级'] == '') or book_key.count('预设班级') == 0) \
and finall_class[boys_id][boy]['分段'] == finall_class[girls_id][girl]['分段']:
finall_class[boys_id][boy], finall_class[girls_id][girl] = finall_class[girls_id][girl],finall_class[boys_id][boy]
every_class[boys_id]['男'] = every_class[boys_id]['男'] - 1
every_class[boys_id]['女'] = every_class[boys_id]['女'] + 1
every_class[girls_id]['男'] = every_class[girls_id]['男'] + 1
every_class[girls_id]['女'] = every_class[girls_id]['女'] - 1
once_flag = 1;
break
if once_flag == 1:
break
if once_flag == 1:
break
if once_flag == 0:
break

# 4. 女生低于低平均值的班级和女生等于高平均值班级互换 (男生班级人数会变成低平均值)
for girls_id in range(need_class):
while every_class[girls_id]['女'] < every_girls_number1 and every_class[girls_id]['男'] > every_boys_number1:
once_flag = 0
for boys_id in range(need_class):
if boys_id != girls_id and every_class[boys_id]['男'] < every_boys_number2 and every_class[boys_id]['女'] > every_girls_number1:
# 在 boys_id 班和 girls_id 班中寻找 分段 相同的男女生交换
for boy in range(len(finall_class[girls_id])):
if finall_class[girls_id][boy]['性别'] == '男'\
and ((book_key.count('预设班级') != 0 and finall_class[girls_id][boy]['预设班级'] == '') or book_key.count('预设班级') == 0):
for girl in range(len(finall_class[boys_id])):
if finall_class[boys_id][girl]['性别'] == '女' \
and ((book_key.count('预设班级') != 0 and finall_class[boys_id][girl]['预设班级'] == '') or book_key.count('预设班级') == 0) \
and finall_class[boys_id][girl]['分段'] == finall_class[girls_id][boy]['分段']:

finall_class[boys_id][girl], finall_class[girls_id][boy] = finall_class[girls_id][boy],finall_class[boys_id][girl]
every_class[boys_id]['男'] = every_class[boys_id]['男'] + 1
every_class[boys_id]['女'] = every_class[boys_id]['女'] - 1
every_class[girls_id]['男'] = every_class[girls_id]['男'] - 1
every_class[girls_id]['女'] = every_class[girls_id]['女'] + 1
once_flag = 1;
break
if once_flag == 1:
break
if once_flag == 1:
break
if once_flag == 0:
break

  1. 随机调整 ,

# 按两个班级的每门课平均分差值是否变小决定是否交换
def check1(max_class_id, p1, min_class_id, p2):
all_range1 = 0
for subject in score_key:
all_range1 = all_range1 + abs(every_class[max_class_id][subject] - every_class[min_class_id][subject])

all_range2 = 0
for subject in score_key:
all_subject = '总分' + subject

temp1_ave = every_class[max_class_id][all_subject] - finall_class[max_class_id][p1][subject] + finall_class[min_class_id][p2][subject]
temp1_ave = temp1_ave / len(finall_class[max_class_id])

temp2_ave = every_class[min_class_id][all_subject] - finall_class[min_class_id][p2][subject] + finall_class[max_class_id][p1][subject]
temp2_ave = temp2_ave / len(finall_class[min_class_id])

all_range2 = all_range2 + abs(temp2_ave - temp1_ave)

# print(all_range1, all_range2)

if all_range2 < all_range1:
return True
else:
return False

# 按总极差变小决定是否交换
def check2(max_class_id, p1, min_class_id, p2):
all_range = 0
all_range1 = 0
for subject in score_key:
all_range1 = all_range1 + abs(every_class[max_class_id][subject] - every_class[min_class_id][subject])

all_range2 = 0
for subject in score_key:
all_subject = '总分' + subject

temp1_ave = every_class[max_class_id][all_subject] - finall_class[max_class_id][p1][subject] + finall_class[min_class_id][p2][subject]
temp1_ave = temp1_ave / len(finall_class[max_class_id])

temp2_ave = every_class[min_class_id][all_subject] - finall_class[min_class_id][p2][subject] + finall_class[max_class_id][p1][subject]
temp2_ave = temp2_ave / len(finall_class[min_class_id])

if temp1_ave > temp2_ave:
temp1_ave, temp2_ave = temp2_ave, temp1_ave
max_score = temp2_ave
min_score = temp1_ave
for i in range(need_class):
if i != max_class_id and i != min_class_id:
if max_score < every_class[i][subject]:
max_score = every_class[i][subject]
if min_score > every_class[i][subject]:
min_score = every_class[i][subject]
all_range = all_range + max_score - min_score
# print(all_range1, all_range2)
return all_range


def change_people(max_class_id, min_class_id, subject):
global finall_all_range
for p1 in range(len(finall_class[max_class_id])):
# 在高分班级中选出高于该科目平均分的人 finall_class[max_class_id][p1]
if finall_class[max_class_id][p1][subject] > every_class[max_class_id][subject]:
for p2 in range(len(finall_class[min_class_id])):
# 预设班级的人不允许交换
if (book_key.count('预设班级') != 0 and finall_class[max_class_id][p1]['预设班级'] == '' and finall_class[min_class_id][p2]['预设班级'] == '') \
or book_key.count('预设班级') == 0:
# 在低分班级中选出低于该科目平均分的人 finall_class[min_class_id][p2]
if finall_class[max_class_id][p1]['性别'] == finall_class[min_class_id][p2]['性别'] \
and finall_class[max_class_id][p1]['分段'] == finall_class[min_class_id][p2]['分段'] \
and finall_class[min_class_id][p2][subject] < every_class[min_class_id][subject]:

# 计算交换后总极差
choice_check = int(random.random() * 2)
checkok = False
if choice_check == 1:
checkok = check1(max_class_id, p1, min_class_id, p2)
else:
temp_all_range = check2(max_class_id, p1, min_class_id, p2)
if temp_all_range < finall_all_range:
checkok = True
# print(temp_range, finall_all_range)
# 若交换后极差变变小则交换
if checkok == True:
finall_class[max_class_id][p1], finall_class[min_class_id][p2] = finall_class[min_class_id][p2], finall_class[max_class_id][p1]
finall_all_range = cal_ave()

6. 下载地址


举报

相关推荐

0 条评论