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

0. 请勿转作商用!有问题可以添加QQ470585226

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格式

06858943e1d0fffa44cf84442ca2728a.png

这里 列A

,

B

A,BA,B 为第一部分内容,列C

D

E

F

G

CDEFGCDEFG 为第二部分内容,列H

HH 为可选内容

其中第一部分内容必须以 "性别"列 结尾,前面的内容可以随意增加,顺序无关

其中第二部分内容必须跟在 “性别” 列之后,全部为每门课的成绩分数,以 "总分"列结尾,其中课程数量可以随意增加,顺序无关

其中第三部分内容为可选内容,若需要预设班级则添加此列,在需要预设班级的人后面标上班级即可,注意若需要分为 6

66 个班,预设班级的数字范围应该是 1

6

1\sim61∼6

3. 最终结果

最终会在代码目录下生成 F

i

n

a

l

l

_

p

l

a

n

.

x

l

s

Finall\_plan.xlsFinall_plan.xls 文件,文件内即为最终方案,每个班级的名单以及全校的总情况

cbde359879f820789bd038806b882c83.png

fc0681144928ab39e7b28122bae29127.png

4. 算法实现

代码整体采用的是随机化算法

主要步骤分 5

55 步

将所有人按分数排序

S

SS 型分班将所有人分成对应需求的 n

e

e

d

_

c

l

a

s

s

need\_classneed_class 个班级(即按照 1

,

2

,

3

,

4

,

5

,

6

,

6

,

5

,

4

,

3

,

2

,

1.....

1,2,3,4,5,6,6,5,4,3,2,1.....1,2,3,4,5,6,6,5,4,3,2,1..... 的顺序分班)。同时将所有人打上分段标记(即 l

e

v

e

l

levellevel 也就是排名前多少人分为一段,此处默认选择 n

e

e

d

_

c

l

a

s

s

need\_classneed_class 超过 30

3030 的最小倍数)

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

e

v

e

l

levellevel 的人交换,后序所有交换中不允许交换有预设班级的人

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

男女都超过高平均值的两个班级互换

女生超过高平均值的班级和男生超过低平均值的班级互换 (男生班级人数会变成高平均值)

男生超过高平均值的班级和女超过低平均值的班级互换 (女班级人数会变成高平均值)

女生低于低平均值的班级和女生等于高平均值班级互换 (男生班级人数会变成低平均值)

随机调整,每次随机挑选两个班,分别枚举两个班的人,若两人 p

1

,

p

2

p1,p2p1,p2 满足条件:

性别相同

l

e

v

e

l

levellevel 相同

没有预设班级

随机在 c

h

e

c

k

1

check1check1 和 c

h

e

c

k

2

check2check2 中挑选一种,若能满足则交换

c

h

e

c

k

1

:

check1:check1: 按两个班级的每门课平均分差值是否变小决定是否交换

c

h

e

c

k

2

:

check2:check2: 按总极差(即所有班级中同一门课的最高平均分减最低平均分之和)是否变小决定是否交换

以上交换共执行 1000

10001000 次,多次测试发现一般最多只能交换几十次,所以一千次一定能使得误差无法再变小,但是因为算法完全随机,所以在任何一次步骤不同都会导致不同的结果,所以考虑随机调整 20

2020 次最终选择误差最小的那一次作为答案,当然 20

2020 次可以根据计算机运行速度以及所需求效率来进行调整,一般一两秒就可以产生结果

5. 代码

将所有人按分数排序

r = Read_Ex()

all_students = r.read_excel()

all_students = sorted(all_students, key=itemgetter('总分'), reverse = True)

need_class = int(input("请输入分班数:"))

S

SS 型分班将所有人分成对应需求的 n

e

e

d

_

c

l

a

s

s

need\_classneed_class 个班级(即按照 1

,

2

,

3

,

4

,

5

,

6

,

6

,

5

,

4

,

3

,

2

,

1.....

1,2,3,4,5,6,6,5,4,3,2,1.....1,2,3,4,5,6,6,5,4,3,2,1..... 的顺序分班)。同时将所有人打上分段标记(即 l

e

v

e

l

levellevel 也就是排名前多少人分为一段,此处默认选择 n

e

e

d

_

c

l

a

s

s

need\_classneed_class 超过 30

3030 的最小倍数)

# 初始化每个班级

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

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

e

v

e

l

levellevel 的人交换,后序所有交换中不允许交换有预设班级的人

# 调整预设班级

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

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

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

随机调整 , c

h

e

c

k

1

check1check1和 c

h

e

c

k

2

check2check2 的实现

# 按两个班级的每门课平均分差值是否变小决定是否交换

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. 下载地址

当然也可以加本人好友 QQ470585226

https://download.csdn.net/download/jnxxhzz/12853196

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值