题目如下:

最近在开发一个项目,做一个试题系统,其中有一个组卷功能(随机抽题),试题属性有:所属专业、题型、难易度、认知层次等。 现在需要实现随机组卷,抽出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


 


作者:KKcat