小寒的blog
Programming is a darned hard thing—but I am going to like it.

在线考试系统提供了根据教师制定的出题策略随机生成试卷的功能。下面看看出题策略部分的详细类图4-15






图4-15 出题策略模块类图

可以看出该部分和试卷部分的类图有些相似。这里PaperStrategyPaper类对应。PaperStrategy(出题策略)是用来生成Paper的。StrategyContainer是用来生成QuestionContainer的。而StrategyItem(策略项)是用来生成试题Question的。下面先从整体上介绍出题策略的概念。出题策略就是一个生成试卷的模板。这个模板制定了要出几个大题。每个大题包含了那些小题。出几个大题比较简单想要出几个大题就给一个出题策略PaperStrategy对象创建几个StrategyContainer子对象就可以了。关键是每个大题包含那些小题比较复杂。这里引入策略项的概念(StrategyItem)。每个大题可以有多个策略项。每个策略项有个QuestionContent集合和分值(ScoreValue),出题数(Count)属性。策略项的GetQuesitons方法会从引用的QuestionContent对象中随机挑出Count个,并根据ScoreValue属性生成Count个分值为ScoreValueQuestion对象。每个策略项引用的多个QuestionContent对象代表了老师想出的一个知识点中的备选题。下面代码是PaperStrategy类的 GetPaper 方法。该方法创建一个新的Paper对象并遍历每个StrategyContainer子对象。然后将每个StrategyContainer生成的大题QuestionContainer对象加入到新建的Paper对象中。

public Paper GetPaper()
{
      Paper paper 
= new Paper();
      paper.IsSubmited 
= false;
      
foreach (StrategyContainer container in strategyContainers)
      
{
          QuestionContainer qustionContainer 
= container.GetQuestionContainer();
           qustionContainer.Paper 
= paper;
           paper.QuestionContainers.Add(qustionContainer);
                
       }

        
return paper;
}

下面再看看每个StrategyContainer是如何生成QuestionContainer对象的。下面是StrategyContainer类的GetQuestionContainer方法


 public QuestionContainer GetQuestionContainer()
        
{
            QuestionContainer result 
= new QuestionContainer();
            result.Title 
= title;
            
foreach (StrategyItem item in strategyItems)
            
{
                IList
<Question> questions = item.GetQuestions();
                
foreach (Question q in questions)
                
{
                    q.QuestionContainer 
= result;
                    result.Questions.Add(q);
                }

            }

            
return result;
        }

上面方法创建并返回了一个新的QuestionContainer对象,并遍历自己的StrategyItem子对象。将每个StrategyItem生成的试题Question对象全部加入到新建的QuestionContainer对象。

最后再来看下StrategyItem是如何随机地从指定的出题范围返回指定数量的Question对象的。下面是StrategyItemGetQuestions方法。

 public IList<Question> GetQuestions()
        
{
            IList
<Question> result = new List<Question>();
            IList
<int> sourceList = new List<int>();
            
for (int i = 0; i < QuestionContents.Count; i++)
                sourceList.Add(i);
            IList
<int> resultList = RandomNumberHelper.RandomSelect(sourceList, count);
            
foreach (int i in resultList)
            
{
                Question question 
= new Question();
                question.Content 
= QuestionContents[i];
                question.ScoreValue 
= ScoreValue;
                result.Add(question);
            }

            
return result;
        }


该方法根据RandomNumberHelper.RandomSelect来随机的从StrategyItemQuestionContents集合属性中随机的选择指定数目的QuestionContents集合的一个子集。并根据子集来生成一个Question对象的集合并返回。这样就完成了根据出题策略生成试卷的功能。下面介绍下随机出题的算法。即从QuestionContents集合中随机选取指定数目的QuestionContent对象。该集合的定义如下。

IList<QuestionContent> QuestionContents = new List<QuestionContent>();

因为是用List来存储的。所以抽取题目的方法是。随机的选取QuestionContents集合的几个下标。比如QuestionContents5个题目想选两个可以从0~4五个数字中选择两个不同的数字.比如选择了13。然后就可以通过QuestionContents[1]QuestionContents[3]取出要的两个题目。下面给出如何从一个下标的范围中随机选取指定数目下标的算法。

class RandomNumberHelper
{
     
public static IList<int> RandomSelect(IList<int> sourceList, int selectCount)
     
{
         
if (selectCount > sourceList.Count)
           
throw new ArgumentOutOfRangeException("selectCount必需大于sourceList.Count");
         IList
<int> resultList = new List<int>();
        
for (int i = 0; i < selectCount; i++)
        
{
               
int nextIndex = GetRandomNumber(1, sourceList.Count);
               
int nextNumber = sourceList[nextIndex - 1];
               sourceList.RemoveAt(nextIndex 
- 1);
               resultList.Add(nextNumber);
         }

        
return resultList;
    }

    
public static int GetRandomNumber(int minValue, int maxValue)
    
{
          
return random.Next(minValue, maxValue + 1);
    }

    
private static Random random = new Random();
}

RandomSelect方法从给定下标集合sourceList中随机的选取selectCount个并将选取结果放到resultList中返回。该算法比网上用递归的方式实现算法效率要高。因为该算法在从sourceList中选出一个下标后就将该下标从sourceList中删除。所以下次在从sourceList中选的时候就不可能再重复挑出已经挑选过的下标。这样就避免了因为随机数不确定性带来的重复筛选的问题。

posted on 2008-05-20 07:07  xhan  阅读(1081)  评论(0编辑  收藏  举报