遗传算法实现自动组卷、随机抽题
公司客户最近要做一个在线考试系统,其中难点在于自动组卷,因为普通的随机方法难度不好掌握,所以领导希望采用遗传算法。算法研究的任务领导交给了我,希望能在端午放假回来看到结果。而我之前并没有听说过遗传算法(孤陋寡闻),但领导分配的任务我不可能说不做(不做回家种地去),所以硬着头皮接了下来,开始GOOGLE遗传算法原理。其中一篇博文基于遗传算法自动组卷的实现对我帮助很大,同时我也联系了此博文的作者,希望能得到他的指点。没想到下午就收到回信,说这篇文章只是纯理论的研究。真正的项目早已找不到了。另外信中给了我几篇关于遗传算法的资料,给了我最直接的帮助。废话不多说,下面直接给代码,代码不难,我也不做解释,请有兴趣的童鞋自己研究(代码现在只是在测试阶段,写得很乱,将就着看吧):
代码
using System;
using System.Windows.Forms;
using System.IO;
namespace GA
{
public partial class Form1 : Form
{
TTm[] TP;
int _ts = 0;
int n = 10;
int m = 12;
int Pc = 50; //杂交的概率
int Pm = 80; //变异的概率
decimal _nd = 2;
int[] Fs = { 2, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20 }; //题目分数
int[] Nd = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5 }; //题目难度
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
n = Fs.Length;
m = Fs.Length;
TP = new TTm[n];
var P = TP;
int E, t;
for (int i = 0; i < n; i++)
{
P[i].T = new KT[m];
}
Initialize(P);
if (!textBox4.Text.Equals(""))
_nd = decimal.Parse(textBox4.Text);
t = 0;
E = Evaluate(P);
decimal _result = 0;
while (P[E].f < 100 || _ts < 12 || Math.Round((decimal)P[E].nd / _ts, 2) < _nd) //分数小于100或者题数小于12或者难度小于2继续循环
{
Crossover(P);//杂交
Mutation(P);//变异
E = Evaluate(P);//评估种群
t = t + 1;
textBox1.Text = t.ToString();
textBox2.Text = P[E].f.ToString();
Print(P[E]);//输出
if (_ts != 0)
{
_result = Math.Round((decimal)P[E].nd / _ts, 2);
textBox4.Text = _result.ToString();//(P[E].nd /_ts)
}
Application.DoEvents();//使程序响应事件,避免假死无法退出现象
if (P[E].f == 100 && _ts >= 12 && _result >= _nd) //总分等于100并且题数大于等于12并且难度系数大于等于2停止循环
{
_ts = 0;
break;
}
Select(P);//择优
}
}
/// <summary>
/// 初始化种群
/// </summary>
/// <param name="P"></param>
private void Initialize(TTm[] P)
{
int i, j, G;
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
P[i].T[j].Fs = Fs[j];
P[i].T[j].nd = Nd[j];
P[i].T[j].Se = 0;
}
}
Random rnd = new Random();
int temp = rnd.Next(m);
for (i = 0; i < n; i++)
{
G = 0;
while (Math.Abs(G - 105) > 10 && G < 130)
{
if (P[i].T[temp].Se == 0)
{
P[i].T[temp].Se = 1;
G = G + P[i].T[temp].Fs;
P[i].T[temp].Se = 0;
}
}
}
}
/// <summary>
/// 评估种群
/// </summary>
/// <param name="P"></param>
private int Evaluate(TTm[] P)
{
int i, j, G, D = 0, result = 0;
for (i = 0; i < n; i++)
{
G = 0;
for (j = 0; j < m; j++)
{
if (P[i].T[j].Se == 1)
{
G = G + P[i].T[j].Fs;
D = D + P[i].T[j].nd;
}
}
P[i].f = 100 - Math.Abs(G - 100);
P[i].nd = D;
if (P[i].f > P[result].f && P[i].nd > P[result].nd)
result = i;
}
return result;
}
/// <summary>
/// 杂交
/// </summary>
/// <param name="P"></param>
private void Crossover(TTm[] P)
{
int i = 0, j = 1, k, t;
Random rnd = new Random();
while (i < n - 1)
{
if (rnd.Next(101) > Pc)
{
//for (k = rnd.Next(m); k < m; k++)//一点杂交
for (k = rnd.Next(m); k <= rnd.Next(m); k++)//两点杂交
{
t = P[i].T[k].Se;
P[i].T[k].Se = P[j].T[k].Se;
P[j].T[k].Se = t;
}
}
i += 2; j += 1;
}
}
/// <summary>
/// 变异
/// </summary>
/// <param name="P"></param>
private void Mutation(TTm[] P)
{
int i;
Random rnd = new Random();
for (i = 0; i < n; i++)
{
if (rnd.Next(101) > Pm)
{
P[i].T[rnd.Next(m)].Se = Convert.ToInt32(!Convert.ToBoolean(P[i].T[rnd.Next(m)].Se));
}
}
}
/// <summary>
/// 择优
/// </summary>
/// <param name="P"></param>
private void Select(TTm[] P)
{
int i, j;
TTm Tm;
for (i = 0; i < n - 1; i++) //对种群按优劣排序
{
for (j = i + 1; j < n; j++)
{
if (P[i].f > P[j].f)
{
Tm = P[i];
P[i] = P[j];
P[j] = Tm;
}
}
}
for (i = 0; i <= (n - 1) / 2; i++) //淘汰50%劣等品种
{
P[n - 1 - i] = P[i];
}
}
/// <summary>
/// 输出
/// </summary>
/// <param name="Tm"></param>
private void Print(TTm Tm)
{
string s1, s2;
int i;
_ts = 0;
s1 = "题号:";
s2 = "分值:";
for (i = 0; i < m; i++)
{
if (Tm.T[i].Se == 1)
{
s1 = s1 + (i+1) + " ";
s2 = s2 + Tm.T[i].Fs + " ";
_ts++;
}
}
textBox3.Text = s1 + "\r\n" + s2 + "\r\n题数:" + _ts;
}
}
public struct KT
{
public int Fs;
public int nd;
public int Se;
}
public struct TTm
{
public KT[] T;
public int f;
public int nd;
}
}
using System.Windows.Forms;
using System.IO;
namespace GA
{
public partial class Form1 : Form
{
TTm[] TP;
int _ts = 0;
int n = 10;
int m = 12;
int Pc = 50; //杂交的概率
int Pm = 80; //变异的概率
decimal _nd = 2;
int[] Fs = { 2, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20 }; //题目分数
int[] Nd = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5 }; //题目难度
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
n = Fs.Length;
m = Fs.Length;
TP = new TTm[n];
var P = TP;
int E, t;
for (int i = 0; i < n; i++)
{
P[i].T = new KT[m];
}
Initialize(P);
if (!textBox4.Text.Equals(""))
_nd = decimal.Parse(textBox4.Text);
t = 0;
E = Evaluate(P);
decimal _result = 0;
while (P[E].f < 100 || _ts < 12 || Math.Round((decimal)P[E].nd / _ts, 2) < _nd) //分数小于100或者题数小于12或者难度小于2继续循环
{
Crossover(P);//杂交
Mutation(P);//变异
E = Evaluate(P);//评估种群
t = t + 1;
textBox1.Text = t.ToString();
textBox2.Text = P[E].f.ToString();
Print(P[E]);//输出
if (_ts != 0)
{
_result = Math.Round((decimal)P[E].nd / _ts, 2);
textBox4.Text = _result.ToString();//(P[E].nd /_ts)
}
Application.DoEvents();//使程序响应事件,避免假死无法退出现象
if (P[E].f == 100 && _ts >= 12 && _result >= _nd) //总分等于100并且题数大于等于12并且难度系数大于等于2停止循环
{
_ts = 0;
break;
}
Select(P);//择优
}
}
/// <summary>
/// 初始化种群
/// </summary>
/// <param name="P"></param>
private void Initialize(TTm[] P)
{
int i, j, G;
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
P[i].T[j].Fs = Fs[j];
P[i].T[j].nd = Nd[j];
P[i].T[j].Se = 0;
}
}
Random rnd = new Random();
int temp = rnd.Next(m);
for (i = 0; i < n; i++)
{
G = 0;
while (Math.Abs(G - 105) > 10 && G < 130)
{
if (P[i].T[temp].Se == 0)
{
P[i].T[temp].Se = 1;
G = G + P[i].T[temp].Fs;
P[i].T[temp].Se = 0;
}
}
}
}
/// <summary>
/// 评估种群
/// </summary>
/// <param name="P"></param>
private int Evaluate(TTm[] P)
{
int i, j, G, D = 0, result = 0;
for (i = 0; i < n; i++)
{
G = 0;
for (j = 0; j < m; j++)
{
if (P[i].T[j].Se == 1)
{
G = G + P[i].T[j].Fs;
D = D + P[i].T[j].nd;
}
}
P[i].f = 100 - Math.Abs(G - 100);
P[i].nd = D;
if (P[i].f > P[result].f && P[i].nd > P[result].nd)
result = i;
}
return result;
}
/// <summary>
/// 杂交
/// </summary>
/// <param name="P"></param>
private void Crossover(TTm[] P)
{
int i = 0, j = 1, k, t;
Random rnd = new Random();
while (i < n - 1)
{
if (rnd.Next(101) > Pc)
{
//for (k = rnd.Next(m); k < m; k++)//一点杂交
for (k = rnd.Next(m); k <= rnd.Next(m); k++)//两点杂交
{
t = P[i].T[k].Se;
P[i].T[k].Se = P[j].T[k].Se;
P[j].T[k].Se = t;
}
}
i += 2; j += 1;
}
}
/// <summary>
/// 变异
/// </summary>
/// <param name="P"></param>
private void Mutation(TTm[] P)
{
int i;
Random rnd = new Random();
for (i = 0; i < n; i++)
{
if (rnd.Next(101) > Pm)
{
P[i].T[rnd.Next(m)].Se = Convert.ToInt32(!Convert.ToBoolean(P[i].T[rnd.Next(m)].Se));
}
}
}
/// <summary>
/// 择优
/// </summary>
/// <param name="P"></param>
private void Select(TTm[] P)
{
int i, j;
TTm Tm;
for (i = 0; i < n - 1; i++) //对种群按优劣排序
{
for (j = i + 1; j < n; j++)
{
if (P[i].f > P[j].f)
{
Tm = P[i];
P[i] = P[j];
P[j] = Tm;
}
}
}
for (i = 0; i <= (n - 1) / 2; i++) //淘汰50%劣等品种
{
P[n - 1 - i] = P[i];
}
}
/// <summary>
/// 输出
/// </summary>
/// <param name="Tm"></param>
private void Print(TTm Tm)
{
string s1, s2;
int i;
_ts = 0;
s1 = "题号:";
s2 = "分值:";
for (i = 0; i < m; i++)
{
if (Tm.T[i].Se == 1)
{
s1 = s1 + (i+1) + " ";
s2 = s2 + Tm.T[i].Fs + " ";
_ts++;
}
}
textBox3.Text = s1 + "\r\n" + s2 + "\r\n题数:" + _ts;
}
}
public struct KT
{
public int Fs;
public int nd;
public int Se;
}
public struct TTm
{
public KT[] T;
public int f;
public int nd;
}
}
下面的图是运行结果: