模板抽题实现

   考试中我们需要根据学生的状态判断是否需要抽题,如果考生状态为已抽题,说明学生为二次登陆,我们联合各个表查出学生对应的大题记录即可;如果考生状态为已交卷,说明学生考试完毕,我们直接提示学生该科已作答完毕即可,如果学生状态为未抽题,这时候又分为两种情况,如果该科考试对应的是试卷的话,我们直接从库中联合几个表查出相应的试卷即可,其过程跟二次登陆类似,比较复杂的是模板抽题,模板提供的就是一套规则,如模板告诉我们试卷上有填空题和选择题两个大题,这两个大题各有10个小题,我们需要根据模板的规则去数据库找相应的题目。

    首先我们来看一下模板抽题的大概实现逻辑


    上图中画出了模板抽题的主要步骤,下面我们结合代码一起来分析一下。

    

 /**
     * 随机抽题算法
     *
     * @param examPaperModel 试卷信息model
     * @param mainList       某课程题干信息list
     * @param chapterList    章节信息list
     * @return
     */
    上图是抽题方法的传入参数,试卷信息是指模板上要求的各种信息。包括试卷的总分数、大题个数、小题个数、每个题的最高难度等级等。mainList中包含某课程下题库中的所有题干信息、chapterList中包含该试卷下所有的章节信息,如果该试卷共包含多少张的内容,每章的比例等。


 //region 初始化数据:章节分布、题型等级分布、题干大小

            //在试卷中获得题型集合
            List<ExamTemPaperQuestionTypeModel> typeModelList = examPaperModel.getPaperQuestionTypeList();

            //region 为试卷上每个大题以及小题分配空间
            //声明变量,用于存放试卷上的所有题干
            ExamQuestionMainModel[][] typeMain;
            //分配试卷上每个大题的存储空间
            typeMain = new ExamQuestionMainModel[typeModelList.size()][];
            //分配试卷上每个题型下每个小题的存储空间
            for (int i = 0; i < typeModelList.size(); i++) {
                typeMain[i] = new ExamQuestionMainModel[typeModelList.get(i).getQuestionTypeNum()];
            }
            //endregion

            //region 为试卷上每个大题中小题的难度等级分配空间
            //声明变量,存放每个大题的难度等级,并分配空间
            int[][] typeDegrees = new int[typeModelList.size()][];
            //声明变量,用于存放每个大题的所有小题的难度等级
            int[][] tempTypeDegrees = new int[typeModelList.size()][];
            for (int i = 0; i < typeModelList.size(); i++) {

                //该大题所有小题的最高难度等级
                int maxDegree = typeModelList.get(i).getQuestionMaxDegree();
                //该大题期望分值
                double questionHopeScore = typeModelList.get(i).getQuestionTypeScore() * (examPaperModel.getHopeScore() / examPaperModel.getScore());
                //该大题总分数
                double questionScore = typeModelList.get(i).getQuestionTypeScore();
                //该大题的所有小题个数
                int questionNum = typeModelList.get(i).getQuestionTypeNum();
                //返回每个难度等级下有几道题
                int[] degrees = scoreDistributionService.distribution(maxDegree, questionHopeScore, questionScore, questionNum);
                //保存该题型下所有小题的等级分布
                typeDegrees[i] = degrees;
                //临时变量,用于保存已经抽取的题的难度等级,初始值赋值为0
                tempTypeDegrees[i] = degrees;
                for (int j = 0; j < tempTypeDegrees[i].length; j++) {
                    tempTypeDegrees[i][j] = 0;
                }
            }
            //endregion

            //region 为每章分配内存空间,并填充每章所对应的分数
            //获取试卷的总分数
            double paperScore = examPaperModel.getScore();
            //声明变量,用于存放每个章节的分数,并分配空间
            double[] chapterScore = new double[chapterList.size()];
            //声明变量,用于存放已选题每个章节的分数,并分配空间
            double[] tempChapterScore = new double[chapterList.size()];

            for (int i = 0; i < chapterList.size(); i++) {
                //填充每个章节的分数
                chapterScore[i] = chapterList.get(i).getChapterRatio() * paperScore;
                //已选题,初始值为0
                tempChapterScore[i] = 0;
            }
            //endregion
            //endregion

            //遍历试卷上每个大题
            for (int i = 0; i < typeModelList.size(); i++) {

                //region 得到题库中该题型的所有题干集合

                //用于存放符合条件的类型的题干集合(如选择题的所有题干)
                List<ExamQuestionMainModel> typeMainList = new ArrayList<>();
                //获取该课程下某类大题的所有题干
                for (int j = 0; j < mainList.size(); j++) {
                    if (typeModelList.get(i).getQuestionTypeId().equals(mainList.get(j).getQuestionTypeId())) {
                        typeMainList.add(mainList.get(j));
                    }
                }
                //endregion
    上面代码得作用是定义存放章节信息、题型等级信息、题干信息的变量,并分配存储空间,上面的每行代码我基本都写了注释,这里不再赘述,我要重点说一下的是在等级分布中调用了distribution方法,这个方法是老师写的方法,传入参数是该题型最高难度等级、该题型期望分值、该题型总分数、该题型小题个数,返回值为一个数组,保存的是每个难度等级下小题的个数。


//region 得到题库中该题型的所有题干集合

                //用于存放符合条件的类型的题干集合(如选择题的所有题干)
                List<ExamQuestionMainModel> typeMainList = new ArrayList<>();
                //获取该课程下某类大题的所有题干
                for (int j = 0; j < mainList.size(); j++) {
                    if (typeModelList.get(i).getQuestionTypeId().equals(mainList.get(j).getQuestionTypeId())) {
                        typeMainList.add(mainList.get(j));
                    }
                }
                //endregion
    得到对应题型的题干集合。根据获取题干的数量与试卷上某大题中所有小题数量对比,执行不同方法,下面我们重点说我们随机抽题的代码。


 //产生随机数,用于作为所抽题的索引
                        int typeMainIndex = random.nextInt(typeMainList.size());
                        Set<Integer> randomcollct = new HashSet<>();

                        //利用set结合的特性,防止产生的随机数有重复的
                        boolean flag = randomcollct.add(typeMainIndex);
                        if (flag == false) {
                            j--;
                            continue;
                        }
产生不重复的随机数,用于随机抽取题干list中的题目。


 //根据抽取的 typeMainIndex 索引进行判断

                        ExamQuestionMainModel examMain = typeMainList.get(typeMainIndex);

                        //region 等级分布条件判断
                        //获得当前题干的难度等级
                        int degree = examMain.getDegree();
                        //定义变量用于存放某难度等级的题型个数
                        int tempTypeDegreeNumber = tempTypeDegrees[i][degree] + examMain.getBlankCount();
                        //当前题型个数与试卷要求的题型个数对比
                        if (tempTypeDegreeNumber > typeDegrees[i][degree]) {
                            //说明该难度等级的题目已抽取完毕
                            j--;
                            continue;
                        }
                        //endregion
    判断等级分布是否符合条件。


  //region 章节比例条件判断
                        String mainChapter = examMain.getChapter();//获取题干章节
                        //定义章节索引变量
                        int chapterIndex = -1;
                        //遍历所有章节
                        for (int k = 0; k < chapterList.size(); k++) {
                            //获得本章节在章节集合中的索引
                            if (chapterList.get(k).getChapter().equals(mainChapter)) {
                                chapterIndex = k;
                                break;
                            }
                        }
                        //计算平均每个题的分数
                        double typeMainScore = typeMainList.get(typeMainIndex).getScore() / typeModelList.get(typeMainIndex).getQuestionTypeNum();
                        //计算该小题实际的分数(该题有两个空则按两个小题计算)
                        double mainScore = typeMainScore * examMain.getBlankCount();

                        //定义变量并赋值(本章节已选题的分数+该小题分数)
                        double chapterMainScore = tempChapterScore[chapterIndex] + mainScore;
                        //判断现在该分数是否超过该章要求的总分数
                        if (chapterMainScore > chapterScore[chapterIndex]) {
                            //说明该章节的题目已经抽取完毕
                            j--;
                            continue;
                        }

                        //endregion
    判断章节分布是否满足。


 //保存已选题个数
                        tempTypeDegrees[i][degree] = tempTypeDegreeNumber;
                        //保存已选题本章节累计总分数
                        tempChapterScore[chapterIndex] = chapterMainScore;
                        //将题干保存到二维数组中
                        typeMain[i][j] = examMain;
    如果章节分布和等级分布都满足,则保存该题。


    上面是根据模板抽卷的整体逻辑,但是并不是完整的代码,我只是写了一些主要的代码。如果在以后的测试中发现问题,再来继续更新。













  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值