近日,需要进行一次大型考试的考场安排工作,遂做了一番深究,并成功实现了一种考场排定的算法。
安排考生的基本思想是:对于报名人数较多的报名点的考生,应尽量分布在所有考场中去,这样可以使得同一考场,同一报名点人数较少。
算法的基本思想是:依照每场人数将报名库中的学生依照一定的次序排定在从首场至尾场的考室中去。另外,还需要打乱其报名号。举例来说的话,如果有100个考场,每考场30人,就是依次把按一定顺序的考生放入第1场、第2场……,直至100场,放30轮即可完成。那么,考生的顺序怎么来确定呢?
考生安排顺序的确定取决于两个方面:1.考生所在报名点;2.为该考生随机生成的随机数。具体来说,就是从报名库中取考生的顺序,查询获取考生依据的排序关键字为两个:“随机数”、报名点。
报名点顺序的产生办法:从报名库中查询到所有报名点的考生数,根据各报名点的考生数从多到少,给报名点赋予顺序号0、1、2、……
考虑到需要排定的考生较多,如果一次从数据库中查询所有的考生,放到内存中排定,则内存的容量可能吃紧。所以每次都直接从数据库中查询,排定时将考场号和座位号存入数据库中。其基本程序的实现如下(此程序使用pony的ORM,考生报名库为postgreSQL。github库:https://github.com/cloveses/practice.git):
import random,math
from pony.orm import *
__author__ = "cloveses"
DB_PARAMS = {
'provider':'postgres',
'user':'postgres',
'password':'123456',
'host':'localhost',
'database':'test'
}
db = Database()
class StudArrg(db.Entity):
signid = Required(str)
name = Required(str)
schcode = Required(str)
sch = Required(str)
sch_seq = Optional(int)
room_code = Optional(int)
number = Optional(int)
sturand = Optional(float)
db.bind(**DB_PARAMS)
db.generate_mapping(create_tables=True)
ROOM_UNIT = 30
# 为所有考生设定随机数,以打乱报名号
@db_session
def set_rand():
for s in StudArrg.select():
s.sturand = random.random() * 10000
# 查询获取考生数、计算考场数、尾场人数、各报名学校人数
@db_session
def get_sch_data(room_unit=ROOM_UNIT):
totals = count(StudArrg.select())
rooms = math.ceil(totals / room_unit)
tails = totals % room_unit
sch_data = select((s.schcode,count(s.name)) for s in StudArrg)[:]
sch_data.sort(key=lambda s:s[1],reverse=True)
return (rooms,tails),sch_data
# 为报名学生设置序号(报名人数较多优先排定,让学生均匀分布各考场)
@db_session
def set_sch_seq(sch_seq):
for index,sch in enumerate(sch_seq):
for s in select(s for s in StudArrg if s.schcode == sch[0]):
s.sch_seq = index
# 安排考生的考场和座位号
@db_session
def arrange_room():
rooms,sch_data = get_sch_data()
set_rand()
set_sch_seq(sch_data)
# print(rooms)
totals,tails = rooms
# 安排定考室
for i in range(tails):
rc = 0
while True:
stud = StudArrg.select(lambda s:s.room_code <= 0 or s.room_code is None).order_by(StudArrg.sturand).order_by(StudArrg.sch_seq).first()
if stud:
stud.room_code = rc + 1
rc += 1
if rc >= totals:
break
totals -= 1 ## 去除尾场考生安排
for i in range(ROOM_UNIT - tails):
rc = 0
while True:
stud = StudArrg.select(lambda s:s.room_code <= 0 or s.room_code is None).order_by(StudArrg.sturand).order_by(StudArrg.sch_seq).first()
if stud:
stud.room_code = rc + 1
rc += 1
if rc >= totals:
break
# 排定座位号
for i in range(totals):
arrange_seat(i+1)
# 排定一场座位号(考点学生从多到少排列后确定)
@db_session
def arrange_seat(roomth):
studs = StudArrg.select(lambda s:s.room_code == roomth)[:]
schs = [s.schcode for s in studs]
sch_num_dict = {sch:schs.count(sch) for sch in schs}
studs.sort(key=lambda s:sch_num_dict[s.schcode],reverse=True)
half = math.ceil(len(studs) / 2)
res = []
for x,y in zip(studs[:half],studs[half:]):
res.extend([x,y])
for i,s in enumerate(res):
s.number = i + 1
if __name__ == '__main__':
arrange_room()