多条件试卷提取的问题(附带测试代码)
问题来自http://q.cnblogs.com/q/38789/
题目如下:
最近在开发一个项目,做一个试题系统,其中有一个组卷功能(随机抽题),试题属性有:所属专业、题型、难易度、认知层次等。 现在需要实现随机组卷,抽出100道题满足以下条件:
条件1:
所属专业: 内科 20% 外科 30% 口腔科 25% 神经科 25。
条件2:
题型: 单选题 30% 多选题 40% 简答题 30%
条件3:
难易度: 难 20% 中 60% 易 20%
条件4:
认知层次: 记忆25% 应用40% 理解 35%
这类题在博问看到了好几次,花了点时间写了下面的代码希望能帮助到需要的人。
其实多条件问题之所以难就是在效率上,所以一般是反过来解决,看条件挑符合的,测试中因为是随机生成的样本库,随机了几次都打不到100题,所以直接生成相应的题目。
下面类实现了两种场景的效率考虑,如果是遍历比较快的可以直接调用AddTest全加一遍,如果是查询比较快的可以通过KindOfTestNeed来提取查询条件,目前的代码生成的结果每次是一样的,如果有真实的库可以采取查询结果的随机化和KindOfTestNeed条件的随机化来生成不同的卷子。
实际引用可以参考Artwl的文章:
直接上代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using FizzWare.NBuilder; using Xunit; namespace ExerciseGenerateSystem { //条件1: //所属专业: 内科 20% 外科 30% 口腔科 25% 神经科 25。 //条件2: //题型: 单选题 30% 多选题 40% 简答题 30% //条件3: //难易度: 难 20% 中 60% 易 20% //条件4: //认知层次: 记忆25% 应用40% 理解 35% public class ExerciseItem { public long Id { get; set; } public Major Major { get; set; } public TestType TestType { get; set; } public Difficulty Difficulty { get; set; } public Awareness Awareness { get; set; } public override string ToString() { return string.Format("{0}\t{1}\t{2}\t{3}\t{4}", this.Id, this.Major, this.TestType, this.Difficulty, this.Awareness); } } public enum Major { [Description("内科")] Medicine, [Description("外科")] Surgery, [Description("口腔科")] Stomatology, [Description("神经科")] Neurology } public enum TestType { SingleAnswer, MultiAnswer, ShortAnswer } public enum Difficulty { Hard, Middle, Easy } public enum Awareness { Remember, Impl, Understand } public class GenerateMyTest { private const int TestTotal = 100; [Fact] public void DoGenerate() { //var testLibary = Builder<ExerciseItem> // .CreateListOfSize(299999) // .All() // .With(e=>e.Awareness=(Awareness)random(2)) // .With(e => e.Difficulty = (Difficulty)random(2)) // .With(e => e.Major = (Major)random(3)) // .With(e => e.TestType = (TestType)random(2)) // .Build(); var myTest = new MyTest(TestTotal); myTest.AddMajorCondition(Major.Medicine, 0.2); myTest.AddMajorCondition(Major.Surgery, 0.3); myTest.AddMajorCondition(Major.Stomatology, 0.25); myTest.AddMajorCondition(Major.Neurology, 0.25); myTest.AddTestTypeCondidtion(TestType.SingleAnswer, 0.3); myTest.AddTestTypeCondidtion(TestType.MultiAnswer, 0.4); myTest.AddTestTypeCondidtion(TestType.ShortAnswer, 0.3); myTest.AddDifficultyCondition(Difficulty.Hard, 0.2); myTest.AddDifficultyCondition(Difficulty.Middle, 0.6); myTest.AddDifficultyCondition(Difficulty.Easy, 0.2); myTest.AddAwarenessCondition(Awareness.Remember, 0.25); myTest.AddAwarenessCondition(Awareness.Impl, 0.4); myTest.AddAwarenessCondition(Awareness.Understand, 0.35); var kindOfTestNeed = myTest.KindOfTestNeed(); for (var i = 1l; !myTest.IsValid() && kindOfTestNeed != null; i++) { var item = new ExerciseItem { Id = i, Awareness = kindOfTestNeed.Item4, Difficulty = kindOfTestNeed.Item3, Major = kindOfTestNeed.Item1, TestType = kindOfTestNeed.Item2 }; //testLibary.FirstOrDefault( // t => // !myTest.Contains(t) // && t.Major == kindOfTestNeed.Item1 // && t.Difficulty == kindOfTestNeed.Item3 // && t.TestType == kindOfTestNeed.Item2 // && t.Awareness == kindOfTestNeed.Item4); if (item == null) { myTest.Print(); throw new Exception("Libary is not fit this conditions!"); } if (!myTest.AddTest(item)) { kindOfTestNeed = myTest.KindOfTestNeed(); } } myTest.Print(); Assert.True(myTest.Validate()); } Random r = new Random(); private int random(int max) { return r.Next(0, max); } [Fact] public void Test() { Console.WriteLine(123d / 123); } } public class ConditionStats { public ConditionStats(double scale) { this.Scale = scale; } public double Scale; public bool IsEnough { get; set; } public bool IsTooMuch { get; set; } public void Update(long count, long total) { var ret = (double)count / (double)total; IsEnough = ret >= Scale; IsTooMuch = ret > Scale; } } public class MyTest { private readonly IList<ExerciseItem> _exerciseItems = new List<ExerciseItem>(); private readonly long _total; private readonly Dictionary<Major, ConditionStats> _majorConditions = new Dictionary<Major, ConditionStats>(); private readonly Dictionary<TestType, ConditionStats> _testTypeConditions = new Dictionary<TestType, ConditionStats>(); private readonly Dictionary<Difficulty, ConditionStats> _difficultyConditions = new Dictionary<Difficulty, ConditionStats>(); private readonly Dictionary<Awareness, ConditionStats> _awarenessConditions = new Dictionary<Awareness, ConditionStats>(); public MyTest(long total) { _total = total; } public void AddMajorCondition(Major major, double scale) { _majorConditions.Add(major, new ConditionStats(scale)); } public void AddTestTypeCondidtion(TestType testType, double scale) { _testTypeConditions.Add(testType, new ConditionStats(scale)); } public void AddDifficultyCondition(Difficulty difficulty, double scale) { _difficultyConditions.Add(difficulty, new ConditionStats(scale)); } public void AddAwarenessCondition(Awareness awareness, double scale) { _awarenessConditions.Add(awareness, new ConditionStats(scale)); } public bool Contains(ExerciseItem item) { return _exerciseItems.Any(e => e.Id == item.Id); } public bool AddTest(ExerciseItem item) { _exerciseItems.Add(item); UpdateStats(item); if (IsTooMuch()) { _exerciseItems.Remove(item); UpdateStats(item); return false; } return true; } private int i = 1; public void UpdateStats(ExerciseItem exerciseItem) { i++; var major = _majorConditions[exerciseItem.Major]; major.Update(_exerciseItems.LongCount(e => e.Major == exerciseItem.Major), _total); var testType = _testTypeConditions[exerciseItem.TestType]; testType.Update(_exerciseItems.LongCount(e => e.TestType == exerciseItem.TestType), _total); var difficulty = _difficultyConditions[exerciseItem.Difficulty]; difficulty.Update(_exerciseItems.LongCount(e => e.Difficulty == exerciseItem.Difficulty), _total); var awareness = _awarenessConditions[exerciseItem.Awareness]; awareness.Update(_exerciseItems.LongCount(e => e.Awareness == exerciseItem.Awareness), _total); } public bool IsTooMuch() { return _majorConditions.Any(mc => mc.Value.IsTooMuch) || _testTypeConditions.Any(mc => mc.Value.IsTooMuch) || _difficultyConditions.Any(mc => mc.Value.IsTooMuch) || _awarenessConditions.Any(mc => mc.Value.IsTooMuch); } public Tuple<Major, TestType, Difficulty, Awareness> KindOfTestNeed() { if (_majorConditions.Any(mc => !mc.Value.IsEnough) && _testTypeConditions.Any(mc => !mc.Value.IsEnough) && _difficultyConditions.Any(mc => !mc.Value.IsEnough) && _awarenessConditions.Any(mc => !mc.Value.IsEnough)) { return new Tuple<Major, TestType, Difficulty, Awareness> ( _majorConditions.First(mc => !mc.Value.IsEnough).Key, _testTypeConditions.First(mc => !mc.Value.IsEnough).Key, _difficultyConditions.First(mc => !mc.Value.IsEnough).Key, _awarenessConditions.First(mc => !mc.Value.IsEnough).Key ) ; } return null; } public bool Validate() { return _majorConditions.All(mc => mc.Value.IsEnough) && _testTypeConditions.All(mc => mc.Value.IsEnough) && _difficultyConditions.All(mc => mc.Value.IsEnough) && _awarenessConditions.All(mc => mc.Value.IsEnough); } public bool IsValid() { return _total == _exerciseItems.LongCount(); } public void Print() { Console.WriteLine("Totoal:{0}", _exerciseItems.Count()); _exerciseItems.ToList().ForEach(Console.WriteLine); } } }
测试结果:
DoGenerate : PassedTotoal:100 1 Medicine SingleAnswer Hard Remember 2 Medicine SingleAnswer Hard Remember 3 Medicine SingleAnswer Hard Remember 4 Medicine SingleAnswer Hard Remember 5 Medicine SingleAnswer Hard Remember 6 Medicine SingleAnswer Hard Remember 7 Medicine SingleAnswer Hard Remember 8 Medicine SingleAnswer Hard Remember 9 Medicine SingleAnswer Hard Remember 10 Medicine SingleAnswer Hard Remember 11 Medicine SingleAnswer Hard Remember 12 Medicine SingleAnswer Hard Remember 13 Medicine SingleAnswer Hard Remember 14 Medicine SingleAnswer Hard Remember 15 Medicine SingleAnswer Hard Remember 16 Medicine SingleAnswer Hard Remember 17 Medicine SingleAnswer Hard Remember 18 Medicine SingleAnswer Hard Remember 19 Medicine SingleAnswer Hard Remember 20 Medicine SingleAnswer Hard Remember 22 Surgery SingleAnswer Middle Remember 23 Surgery SingleAnswer Middle Remember 24 Surgery SingleAnswer Middle Remember 25 Surgery SingleAnswer Middle Remember 26 Surgery SingleAnswer Middle Remember 28 Surgery SingleAnswer Middle Impl 29 Surgery SingleAnswer Middle Impl 30 Surgery SingleAnswer Middle Impl 31 Surgery SingleAnswer Middle Impl 32 Surgery SingleAnswer Middle Impl 34 Surgery MultiAnswer Middle Impl 35 Surgery MultiAnswer Middle Impl 36 Surgery MultiAnswer Middle Impl 37 Surgery MultiAnswer Middle Impl 38 Surgery MultiAnswer Middle Impl 39 Surgery MultiAnswer Middle Impl 40 Surgery MultiAnswer Middle Impl 41 Surgery MultiAnswer Middle Impl 42 Surgery MultiAnswer Middle Impl 43 Surgery MultiAnswer Middle Impl 44 Surgery MultiAnswer Middle Impl 45 Surgery MultiAnswer Middle Impl 46 Surgery MultiAnswer Middle Impl 47 Surgery MultiAnswer Middle Impl 48 Surgery MultiAnswer Middle Impl 49 Surgery MultiAnswer Middle Impl 50 Surgery MultiAnswer Middle Impl 51 Surgery MultiAnswer Middle Impl 52 Surgery MultiAnswer Middle Impl 53 Surgery MultiAnswer Middle Impl 55 Stomatology MultiAnswer Middle Impl 56 Stomatology MultiAnswer Middle Impl 57 Stomatology MultiAnswer Middle Impl 58 Stomatology MultiAnswer Middle Impl 59 Stomatology MultiAnswer Middle Impl 60 Stomatology MultiAnswer Middle Impl 61 Stomatology MultiAnswer Middle Impl 62 Stomatology MultiAnswer Middle Impl 63 Stomatology MultiAnswer Middle Impl 64 Stomatology MultiAnswer Middle Impl 65 Stomatology MultiAnswer Middle Impl 66 Stomatology MultiAnswer Middle Impl 67 Stomatology MultiAnswer Middle Impl 68 Stomatology MultiAnswer Middle Impl 69 Stomatology MultiAnswer Middle Impl 71 Stomatology MultiAnswer Middle Understand 72 Stomatology MultiAnswer Middle Understand 73 Stomatology MultiAnswer Middle Understand 74 Stomatology MultiAnswer Middle Understand 75 Stomatology MultiAnswer Middle Understand 77 Stomatology ShortAnswer Middle Understand 78 Stomatology ShortAnswer Middle Understand 79 Stomatology ShortAnswer Middle Understand 80 Stomatology ShortAnswer Middle Understand 81 Stomatology ShortAnswer Middle Understand 83 Neurology ShortAnswer Middle Understand 84 Neurology ShortAnswer Middle Understand 85 Neurology ShortAnswer Middle Understand 86 Neurology ShortAnswer Middle Understand 87 Neurology ShortAnswer Middle Understand 89 Neurology ShortAnswer Easy Understand 90 Neurology ShortAnswer Easy Understand 91 Neurology ShortAnswer Easy Understand 92 Neurology ShortAnswer Easy Understand 93 Neurology ShortAnswer Easy Understand 94 Neurology ShortAnswer Easy Understand 95 Neurology ShortAnswer Easy Understand 96 Neurology ShortAnswer Easy Understand 97 Neurology ShortAnswer Easy Understand 98 Neurology ShortAnswer Easy Understand 99 Neurology ShortAnswer Easy Understand 100 Neurology ShortAnswer Easy Understand 101 Neurology ShortAnswer Easy Understand 102 Neurology ShortAnswer Easy Understand 103 Neurology ShortAnswer Easy Understand 104 Neurology ShortAnswer Easy Understand 105 Neurology ShortAnswer Easy Understand 106 Neurology ShortAnswer Easy Understand 107 Neurology ShortAnswer Easy Understand 108 Neurology ShortAnswer Easy Understand