关于校内考试的考场座位编排算法的研究
摘要:文章针对校内考试的考场座位编排的复杂情况进行深入浅出的分析,提出切实可行、科学高效的座位编排算法,并编写程序加以实现。
关键词:座位编排;算法
作者单位:陆川县马坡镇第二初级中学
姓名:覃坤
通讯地址:陆川县马坡镇第二初级中学
邮编:537712
电话:133********
E-mail:ylsyz@https://www.wendangku.net/doc/6f15286639.html,
一.引言
为了更好地论叙问题,本文引入了两个概念,解释如下:
校际考试——多个学校共同参与的考试,如中考、高考。
校内考试——单个学校组织的考试,如段考、期考。
考场座位的科学编排是保障考试的公平与质量的重要手段。因此,校际考试,如中考、高考、研考等重大考试,对考场座位均有严格要求,相关算法与软件相当成熟且日益完善。但关于校内考试的相应研究却鲜有耳闻,相关软件也是寥寥无几且功能简陋。
我是一名一线教师,深切感受到校内考试中考座编排工作的意义非常与困难重重,试图尽绵薄之力深入探讨这其中的科学规律,为广大中小学提供有限的经验。(本算法对应的程序发布于本人主页:https://www.wendangku.net/doc/6f15286639.html,,程序在多间学校至今运行良好。)
二.对考场座位编排的分析
1.对校际考试的考场座位编排的分析
相对于校内考试,校际考试的座位编排的复杂度较小,实现的算法也较简单。我把该种算法称为“蛇形分布”算法。举例说明:某年中考,56号考点的考生来自甲中学与乙中学,根据“蛇形分布”算法,考场内的30个座号,从小到大,按甲乙、甲乙……的顺序进行分配,得到的考场座位的编排情况将如图1所示。
图1
(注:白色方框代表甲中学学生,灰色方框代表乙中学学生)仔细观察,可以发现甲、乙中学的学生得到了妥善安排:每位考生的前后左右均无同校的同学,这在某些方面大大增加了作弊的困难。而且该算法的通用性极强,假如参考学校有2个以上,依然能出色地完成编排,参考学校越多,防止作弊的功能越强。2.对校内考试的考场座位编排的分析
“蛇形分布”算法虽然巧妙,却不适用于校内考试。主要原因有二:第一,校际考试将学生按学校间隔分布,而在校内考试却只可能按班或按年级间隔分布,若按年级间隔分布,同列中有多个年级的考生存在,这将给收发试卷带来很大的麻烦;第二,“蛇形分布”算法的实现基于单人单桌,需要在考试前对考室进行布置。而在日常考试中,教室内的桌椅并不搬动,通常是双人双桌,该算法反而为舞弊创造了条件。举例:图2是双人双桌的考场,11号与1、3号是同类别学生,相对于图1距离过近更容易串通作
弊,假设11号考生位于2号的位置,作弊反而不易。
由此,我们必须意识到,校内考试是日常考试,绝对不能生搬硬套校际考试的算法,
校内考试的考务工作应当更加简洁,更节约时间、人力和物力,才能为学校所承受。那么,对于校内考试,怎样编排座位才算科学合理呢?我想,类似于图3所示的考场座位才是比较理想的。
图3(注:041—046是初二班级,051—057是初一班级)观察图3,你会以现这样的考场座位具有以下优点:①座号的编排顺序符合师生的日常称呼习惯,有利于考生快速就座;②相同年级同列,有利于快速收发试卷;③同班学生距离最大化(如座号1、13、25的考生同是041班),有利于防止舞弊。④教室内的桌椅不必搬动,有利于教室在考试与教学环境间的快速转换。
能实现这个考室示意图的算法,还应当有利于生成类似图4的考生通知单(注意观察图4与图3的对应关系)。该单张贴于考生所在班级,使得考务工作得以摒弃准考证号等概念,并能够准确快捷地告知考生考座信息。
图4
其次,算法还应当有利于生成类似图5的考场情况统计表,方便教务处核定试卷数目,为各个考室分发试卷。
最后,算法还应当能适应各班桌椅数目不一的情况,充分使用教室的所有桌椅。因为,在中考、高考中,考生不过是学生中的一部分,所以教室相对充足,每个考室的人数可以做到异常的统一,而在段考、期考中,考生是全校学生,每个教室的每个座位都非常珍贵,决不允许算法进行浪费。
但要实现这样的算法,却是困难重重。该算法虽然适应了师生对座号顺序的日常称呼习惯,却因此在分配座号时面对以下情况,问题的复杂度大大增加:
①参考年级的个数发生变化。举例:如图3所示的考场,只有2个年级参考,但如果年级个数增至3个,考场就应如图5所示,那么座号的分配原则究竟怎样设定,才能无论年级个数的多少,都可遵循相同年级同列的要求,生成类似图5的考室示意图?
图5(注:05X是初一班级,04X是初二班级,03X是初三班级)
②教室内的桌椅的列数发生变化。举例说明:如图5所示,教室是8列双座,一旦变为9列双座,考场就应如图6所示,那座号的分配原则又应当是什么,方能使得不管教室座位的列数怎样变化,都可保证“相同年级同列”,生成类似图6的考室示意图?
图6(注:05X是初一班级,04X是初二班级,03X是初三班级)
①和②的变化情况在日常考试中是很常见的,在某个考室中,可能是2个年级8列双座,在另一个考室中,却可能是3个年级10列双座。显然,“蛇形分布”算法不可能解答这两个问题,适应这样复杂多变的考座编排要求。
尽管校内考试的考座编排是考务工作中的必须环节,但却没有一个成熟的软件来解脱教师繁杂的工作。记得刚毕业那年,教务主任就给我分配了考座编排的任务。我使用了EXCEL来编排,结果奋战了一个通宵,才实现了教务主任关于编排的设想。这种编排手段应属于“手工阶段”。后来,我仔细思考了编排中的重复步骤,将它们编成了宏,编排的时间也迅速缩短到了5个小时。一年后,我又将其中的一些耗时较多的步骤编写为VBA,时间又缩短为3个小时。这期间的水平处于“半自动阶段”。几个月后,我总结出了编排算法,并使用Delphi7将其实现,把编排的时间缩短到了30分钟以内(含学生名册导入、考场设置、打印机的工作时间)。至此,考座编排的手段才算是完全的“自动化”。
现将该算法描述于下。
三、算法的具体描述
为了方便理解算法,首先对算法涉及的数据表(存储结构)进行说明。
伪代码如下:
var
kcmc:string; //考试名称,如“XX年XX中学春期期考”
dqlj:string;//程序的当前路径
//考室示意图所需的数组与考室备注名称
a,b,c:array[1..100] of string;
ksmc,bz:string;
{为了使考室示意图模板能在脱离编绎环境后为用户所修改,数据不自动向报表传递,而改为由程序控制}
procedure TfrmMain.Button9Click(Sender: TObject);
var
i,j,k,L,kcdy,ksrs,temp:integer;//考场定员与考生人数
s:string;
dy,m,n,x,amod:integer;//定员,类型的列数,年级的个数
reportClear:boolean; //是否清除报表
cxb,hz,ym,yy:integer;//次下标,行左,右边空座数目,右移个数
begin
……
//考场的总定员是否足够
if kcdy begin Application.MessageBox('考场的座位不足,请重新设置考室表或考生表。', '信息', MB_OK + MB_DEFBUTTON1 + MB_ICONINFORMATION + MB_APPLMODAL); abort; end; //一、对考室表的设置进行检查************ …… //二、对考生表进行排序 …… //如果不是选择按成绩排序,则进行填充随机数列 …… //根据[考生座位编排顺序选择],开始对考生表进行排序 …… //三、开始编考场座号 //遍历考室表 with QrKsb2 do begin //Sort:='使用次序'; //排序 j:=RecordCount; First; for i:=1 to j do begin memo2.Lines.Clear; x:=1; //必须有一个参考年级 if length(FieldByName('参考年级2').AsString)>0 then inc(x); if length(FieldByName('参考年级3').AsString)>0 then inc(x); dy:=FieldByName('定员').AsInteger; L:=0;//是第一个参考年级 s:=FieldByName('类型').AsString; n:=0; for m:=1 to 2 do if (midstr(s,m,1)>='0') and (midstr(s,m,1)<='9') then inc(n); n:=strtoint(copy(s,1,n)); //类型对应的列数 for k:=1 to 3 do begin s:='参考年级'+inttostr(k); if length(FieldByName(s).AsString)>0 then //该参考年级k不为空 begin memo2.Lines.Add(format('□考室:%s 参考年级:%s---',[FieldByName('名称').AsString,s])); QrTemp.Close; QrTemp.sql.clear; if CheckBox1.Checked then QrTemp.sql.Add(format('select * from 考生表where 年级="%s" and (Not 座号>0) order by 序ASC,班别' ,[FieldByName(s).AsString])) else QrTemp.sql.Add(format('select * from 考生表where 年级="%s" and (Not 座号>0) order by 序ASC,随机数' ,[FieldByName(s).AsString])); QrTemp.open; temp:=QrTemp.RecordCount; inc(L); //存在的参考年级的序号(1开始) for m:=1 to dy do begin //m合适吗? amod:=(m mod n); if amod=0 then amod:=n; amod:=(amod mod x); if amod=0 then amod:=x; if amod=L then //m可用 begin if not QrTemp.Eof then //有考生可供写考座 begin QrTemp.Edit; QrTemp.FieldByName('考室').AsString:=FieldByName('名称').AsString; QrTemp.FieldByName('座号').AsString:=inttostr(m); QrTemp.Post; memo2.Lines.Add(format('□考室:%s %s %s %s', [FieldByName('名称').AsString,s,QrTemp.FieldByName('姓名 ').AsString,inttostr(m)])); QrTemp.Next; //送出考座后,再下一记录 end else begin //否则,插入空置的考座 QrTemp2.close; QrTemp2.sql.clear; QrTemp2.sql.Add('Insert into 考生表([考室],[座号],[年级]) '+ 'values(:p1,:p2,:p3)'); QrTemp2.parameters.ParamByName('p1').Value:=FieldByName('名称').AsString; QrTemp2.parameters.ParamByName('p2').Value:=m; QrTemp2.parameters.ParamByName('p3').Value:=FieldByName(s).AsString; QrTemp2.ExecSQL; end; //if temp<=m end;//if amod end;// for m end;//if length end;//for k next; end; // for i first; end; //with QrKsb2 //四、根据使用次序,生成考室示意图 memo2.Lines.Clear; reportClear:=true; frxReport1.PreviewPages.Clear; //编历考室表 with QrKsb2 do begin j:=RecordCount; First; for i:=1 to j do begin memo2.Lines.Clear; bz:=''; ksmc:=FieldByName('名称').AsString; dy:=FieldByName('定员').AsInteger; s:=FieldByName('类型').AsString; n:=0; for m:=1 to 2 do if (midstr(s,m,1)>='0') and (midstr(s,m,1)<='9') then inc(n); n:=strtoint(copy(s,1,n)); //类型对应的列数 //筛选出考生表的内容 QrTemp.Close; QrTemp.sql.clear; QrTemp.sql.Add('select 班别,姓名from 考生表where 考室=:p1 order by 座号'); QrTemp.parameters.ParamByName('p1').Value:=ksmc; QrTemp.open; temp:=QrTemp.RecordCount; for m:=1 to 100 do begin if m<=temp then begin a[m]:=inttostr(m); b[m]:=QrTemp.FieldByName('班别').AsString; c[m]:=QrTemp.FieldByName('姓名').AsString; QrTemp.Next; memo2.Lines.Add(format('%d %s %s→进入报表',[m,b[m],c[m]])); end else begin a[m]:=''; b[m]:=''; c[m]:=''; end;// if m<=temp end;//for m QrTemp.close; if FieldByName('末行居右').AsBoolean then begin x:=1; //必须有一个参考年级 if length(FieldByName('参考年级2').AsString)>0 then inc(x); if length(FieldByName('参考年级3').AsString)>0 then inc(x); //寻找次下标 for cxb:=100 downto 1 do if length(c[cxb])>0 then Break; hz:=(cxb div n)*n+1; ym:=n-(cxb-hz+1); yy:=(ym div x)*x; if yy>0 then for k:=cxb downto hz do begin a[k+yy]:=a[k]; b[k+yy]:=b[k]; c[k+yy]:=c[k]; a[k]:=''; b[k]:=''; c[k]:=''; end;//for k:=cxb end;// if FieldByName('末行居右').AsBoolean for k:=1 to 3 do begin s:='参考年级'+inttostr(k); if length(FieldByName(s).AsString)>0 then //该参考年级k不为空 begin QrTemp.sql.clear; QrTemp.sql.Add('select count(姓名) as 人数from 考生表where 考室=:p1 and 年级=:p2'); QrTemp.parameters.ParamByName('p1').Value:=ksmc; QrTemp.parameters.ParamByName('p2').Value:=FieldByName(s).AsString; QrTemp.open; temp:=QrTemp.FieldByName('人数').AsInteger; bz:=bz+FieldByName(s).AsString+':'+QrTemp.FieldByName('人数').AsString+#13#10; if temp=0 then jy1:=jy1+'☆考室:['+ksmc+'] 参考年级: ['+FieldByName(s).AsString+']'+#13#10 else if temp<=9 then jy2:=jy2+'★考室:['+ksmc+'] 参考年级:['+FieldByName(s).AsString+ '] 人数:['+inttostr(temp)+']'+#13#10; QrTemp.close; end;//if length end;//for k //根据定员与类型选择报表模板 for m:=7 to 10 do begin temp:=n*m; if temp>=dy then Break; end;//for m s:=dqlj+'考室模板\'+FieldByName('类型').AsString+inttostr(temp)+'.fr3'; with frxReport1 do //载入报表模板 begin Clear; LoadFromFile(s); if PrepareReport(reportClear) then memo2.Lines.Add(format('□考室:%s 示意图创 建成功',[ksmc])); end; reportClear:=false; //不清除报表 next; end; //for i end;// with QrKsb2 memo2.Lines.Clear; //清除信息框 //将报表内的文件另存到考场文件夹中 frxReport1.PreviewPages.SaveToFile(dqlj+'考场文件\'+kcmc+'-考室示意图.fp3'); reportClear:=true; //清除报表 //生成考生通知单****************************** …… //生成考场情况表****************************** …… temp:=ADOQuery1.RecordCount; if temp>0 then //出现无座考生,给出建议 …… begin else application.MessageBox('座位文件生成完毕,请到[打印输出]处打印。','信息',64); end; …… 四.参考文献 无