文章目錄
- 0. 請勿轉作商用!有問題可以添加QQ470585226
- 1. 代碼運行情況
- 2. 需要提供的excel格式
- 3. 最終結果
- 4. 演算法實作
- 5. 代碼
- 6. 下載地址
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格式

這里 列
A
,
B
A,B
A,B 為第一部分內容,列
C
D
E
F
G
CDEFG
CDEFG 為第二部分內容,列
H
H
H 為可選內容
- 其中第一部分內容必須以 "性別"列 結尾,前面的內容可以隨意增加,順序無關
- 其中第二部分內容必須跟在 “性別” 列之后,全部為每門課的成績分數,以 "總分"列結尾,其中課程數量可以隨意增加,順序無關
- 其中第三部分內容為可選內容,若需要預設班級則添加此列,在需要預設班級的人后面標上班級即可,注意若需要分為 6 6 6 個班,預設班級的數字范圍應該是 1 ~ 6 1\sim6 1~6
3. 最終結果
最侄訓在代碼目錄下生成
F
i
n
a
l
l
_
p
l
a
n
.
x
l
s
Finall\_plan.xls
Finall_plan.xls 檔案,檔案內即為最終方案,每個班級的名單以及全校的總情況


4. 演算法實作
代碼整體采用的是隨機化演算法
主要步驟分
5
5
5 步
-
將所有人按分數排序
-
S S S 型分班將所有人分成對應需求的 n e e d _ c l a s s need\_class need_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 level level 也就是排名前多少人分為一段,此處默認選擇 n e e d _ c l a s s need\_class need_class 超過 30 30 30 的最小倍數)
-
調整預設班級,若有人存在預設班級而本人不在對應班級,則跟對應班級同 l e v e l level level 的人交換,后序所有交換中不允許交換有預設班級的人
-
調整每個班級的男女數量(四種情況分別配平,交換男女時保證只能交換同分數段的人)
- 男女都超過高平均值的兩個班級互換
- 女生超過高平均值的班級和男生超過低平均值的班級互換 (男生班級人數會變成高平均值)
- 男生超過高平均值的班級和女超過低平均值的班級互換 (女班級人數會變成高平均值)
- 女生低于低平均值的班級和女生等于高平均值班級互換 (男生班級人數會變成低平均值)
-
隨機調整,每次隨機挑選兩個班,分別列舉兩個班的人,若兩人 p 1 , p 2 p1,p2 p1,p2 滿足條件:
- 性別相同
- l e v e l level level 相同
- 沒有預設班級
- 隨機在
c
h
e
c
k
1
check1
check1 和
c
h
e
c
k
2
check2
check2 中挑選一種,若能滿足則交換
c h e c k 1 : check1: check1: 按兩個班級的每門課平均分差值是否變小決定是否交換
c h e c k 2 : check2: check2: 按總極差(即所有班級中同一門課的最高平均分減最低平均分之和)是否變小決定是否交換
以上交換共執行 1000 1000 1000 次,多次測驗發現一般最多只能交換幾十次,所以一千次一定能使得誤差無法再變小,但是因為演算法完全隨機,所以在任何一次步驟不同都會導致不同的結果,所以考慮隨機調整 20 20 20 次最終選擇誤差最小的那一次作為答案,當然 20 20 20 次可以根據計算機運行速度以及所需求效率來進行調整,一般一兩秒就可以產生結果
5. 代碼
- 將所有人按分數排序
r = Read_Ex()
all_students = r.read_excel()
all_students = sorted(all_students, key=itemgetter('總分'), reverse = True)
need_class = int(input("請輸入分班數:"))
- S S S 型分班將所有人分成對應需求的 n e e d _ c l a s s need\_class need_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 level level 也就是排名前多少人分為一段,此處默認選擇 n e e d _ c l a s s need\_class need_class 超過 30 30 30 的最小倍數)
# 初始化每個班級
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 level level 的人交換,后序所有交換中不允許交換有預設班級的人
# 調整預設班級
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 check1 check1和 c h e c k 2 check2 check2 的實作
# 按兩個班級的每門課平均分差值是否變小決定是否交換
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/80510.html
標籤:其他
