之前我们讨论了如何自己构建一个分布式评测系统来提升OJ判题能力。这一篇来探讨一下丰富OJ题目,出题并不是难事------能参考的题目很多(如果各种题目搬来就用那么没有必要搞什么OJ了),但测试数据往往是一个OJ的核心资产;有时候我们出一些题目也需要自己构建数据。自己构建测试数据,主要有两个思路:
1、写一个程序随机,这可能是题目量少的时候的常用做法,但题目一多就让人非常难受。
2、使用第三方数据生成器,这玩意不少,但有的设计的和新学习一门语言差不多。
本文给出一种思路来实现一个非图论题目数据生成器,类似的也可以构建图论生成器。
一、设计
让我们思考一下,普通题目数据有什么样的:
第一行,有T,N,M,K?......
接下来N行,有什么什么......
也就是说,我们定义一些数据,让他们在第一行显式;然后根据某些数据的随机值确定下面有多少行或者多少列;更复杂一点的多组数据,无非是把一行前面的若干值后面增加一个换行。傻瓜式,界面式的,用规则来约束随机性就足够胜任:


因为我很懒啊,所以很多东西并没有做多次约束,例如界面上那个变量,实际上现在仅有A。我们实际上是把数据生成规则做了一个图形化,然后只需要:
1、图形化填写规则
2、解释并生成数据
3、自动编译代码并使用随机数据生成对应输出
4、保存
二、规则代码实现
vbnet
Public Class frmRulaInData
Protected Friend Structure MainRulaInfo
Friend GroupCount As Integer '这一段有几个数据组
Friend VariableName As String '变量名
Friend Visible As Boolean '变量可见性
Friend MinValue As String '最小值
Friend MaxValue As String '最大值
Friend Absolute As Boolean '最大最小值是否是用绝对值表示的
Friend Shared lastValue As New Dictionary(Of String, Decimal) '上次随机之后的结果
End Structure
Protected Friend Structure ColRulaInfo
Dim ColCountVariable As String '生成多少列------变量
Dim ColCountOffset As Integer '生成多少列------常量(与变量相加得到该行规则生成的列数)
Dim ColType As String '数据类型(整数、小数、字符串)
Dim ColVariableRula As String '提供范围限制的变量名
Dim ColValueRange As String '有效值生成器
Dim ColNoRepeatInRow As Boolean '在本行内不可出现相同值
Dim ColNoRepeatInCol As Boolean '在各行相同列不可出现想通值
Dim ColSpace As Integer '后续空格个数
End Structure
Protected Friend Structure RowRulaInfo
Dim RowCountVariable As String '生成多少行------变量
Dim RowCountOffset As Integer '常量
Dim RowRula As String '特殊操作
Dim RowRulaArg1 As String '特殊操作参数1
Dim RowRulaArg2 As String '特殊操作参数2
End Structure
'数据组规则:下标为段号,数据为生成数据个数
Protected Friend DataGroup As New List(Of Integer)
'首行和变量规则:下标为段号,数据为该段的变量规则
Protected Friend MainRula As New List(Of List(Of MainRulaInfo))
'后续列规则:下标为列号,数据为列限制信息
Protected Friend ColRula As New List(Of ColRulaInfo)
'后续行规则:下标为行号+1,数据为行规范化信息
Protected Friend RowRula As New List(Of RowRulaInfo)
'是否是第一次显示
Protected Friend LastShow As Boolean = False
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Not LastShow Then
butDataGroupAdd_Click(Nothing, Nothing)
butHandPulaAdd_Click(Nothing, Nothing)
butColRulaAdd_Click(Nothing, Nothing)
butRowRulaAdd_Click(Nothing, Nothing)
LastShow = True
End If
End Sub
#Region "数据组预定义"
'添加一个组定义
Private Sub butDataGroupAdd_Click(sender As Object, e As EventArgs) Handles butDataGroupAdd.Click
Dim rw = dgvDataGroup.Rows.Add()
If rw = 0 Then
dgvDataGroup.Rows(rw).Cells("colDataGroupIndex").Value = 1
Else
dgvDataGroup.Rows(rw).Cells("colDataGroupIndex").Value = dgvDataGroup.Rows(rw - 1).Cells("colDataGroupIndex").Value + 1
End If
dgvDataGroup.Rows(rw).Cells("colDataCount").Value = 1
MainRulaUpData()
End Sub
'删除一个组定义
Private Sub butDataGroupDel_Click(sender As Object, e As EventArgs) Handles butDataGroupDel.Click
If dgvDataGroup.RowCount > 1 Then
dgvDataGroup.Rows.RemoveAt(dgvDataGroup.RowCount - 1)
MainRulaUpData()
End If
End Sub
#End Region
#Region "首行变量预定义"
'添加A-Z变量
Private Sub butHandPulaAdd_Click(sender As Object, e As EventArgs) Handles butHandPulaAdd.Click
If dgvHandPula.RowCount < 26 Then
Dim rw = dgvHandPula.Rows.Add()
dgvHandPula.Rows(rw).Cells("colVariableName").Value = "@PulaVar_" & Chr(Asc("A") + rw)
dgvHandPula.Rows(rw).Cells("colVisible").Value = True
MainRulaUpData()
End If
End Sub
Private Sub butHandPulaDel_Click(sender As Object, e As EventArgs) Handles butHandPulaDel.Click
If dgvHandPula.RowCount > 1 Then
dgvHandPula.Rows.RemoveAt(dgvHandPula.RowCount - 1)
MainRulaUpData()
End If
End Sub
'更新首行定义
Private Sub MainRulaUpData()
Dim rw As Integer
dgvMainRula.Rows.Clear()
For i As Integer = 0 To dgvDataGroup.RowCount - 1
For j As Integer = 0 To dgvHandPula.RowCount - 1
rw = dgvMainRula.Rows.Add()
dgvMainRula.Rows(rw).Cells("colGroupID").Value = dgvDataGroup.Rows(i).Cells("colDataGroupIndex").Value
dgvMainRula.Rows(rw).Cells("colVariable").Value = dgvHandPula.Rows(j).Cells("colVariableName").Value
dgvMainRula.Rows(rw).Cells("colMinValue").Value = "|0|0"
dgvMainRula.Rows(rw).Cells("colMaxValue").Value = "|0|0"
Next
Next
End Sub
Private Sub dgvMainRula_CellMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvMainRula.CellMouseClick
If e.RowIndex < 0 Then Return
Dim cell = dgvMainRula.Rows(e.RowIndex).Cells(e.ColumnIndex)
If dgvMainRula.Columns(e.ColumnIndex).Name = "colMinValue" OrElse dgvMainRula.Columns(e.ColumnIndex).Name = "colMaxValue" Then
If e.Button = MouseButtons.Left Then
Dim frmPick As New frmValuePick
frmPick.DialogResultString = cell.Value
If frmPick.ShowDialog(Me) = DialogResult.OK Then
cell.Value = frmPick.DialogResultString
End If
End If
If e.Button = MouseButtons.Right Then
cell.Value = Nothing
End If
End If
End Sub
#End Region
#Region "输入数据生成规则"
'增加列规则
Private Sub butColRulaAdd_Click(sender As Object, e As EventArgs) Handles butColRulaAdd.Click
Dim rw = dgvColPula.Rows.Add()
dgvColPula.Rows(rw).Cells("colColCountVariable").Value = String.Empty
dgvColPula.Rows(rw).Cells("colColCountOffset").Value = 1
dgvColPula.Rows(rw).Cells("colColSpace").Value = 1
CType(dgvColPula.Rows(rw).Cells("colColType"), DataGridViewComboBoxCell).Value = "整数"
End Sub
'删除列规则
Private Sub butColRulaDel_Click(sender As Object, e As EventArgs) Handles butColRulaDel.Click
If dgvColPula.RowCount > 1 Then
dgvColPula.Rows.RemoveAt(dgvColPula.RowCount - 1)
End If
End Sub
'变量选择
Private Sub dgvColPula_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgvColPula.EditingControlShowing
If dgvColPula.Columns(dgvColPula.CurrentCell.ColumnIndex).Name = "colColVariableRula" OrElse dgvColPula.Columns(dgvColPula.CurrentCell.ColumnIndex).Name = "colColCountVariable" Then
Dim ctl As ComboBox = e.Control
ctl.Items.Clear()
ctl.Items.Add(String.Empty)
For i As Integer = 0 To dgvHandPula.RowCount - 1
ctl.Items.Add(dgvHandPula.Rows(i).Cells("colVariableName").Value)
Next
End If
End Sub
Private Sub dgvRowPula_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgvRowPula.EditingControlShowing
If dgvRowPula.Columns(dgvRowPula.CurrentCell.ColumnIndex).Name = "rowRowCountVariable" Then
Dim ctl As ComboBox = e.Control
ctl.Items.Clear()
ctl.Items.Add(String.Empty)
For i As Integer = 0 To dgvHandPula.RowCount - 1
ctl.Items.Add(dgvHandPula.Rows(i).Cells("colVariableName").Value)
Next
End If
End Sub
'数据筛选规则
Private Sub dgvColPula_CellMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvColPula.CellMouseClick
If e.RowIndex < 0 Then Return
Dim cell = dgvColPula.Rows(e.RowIndex).Cells(e.ColumnIndex)
If dgvColPula.Columns(e.ColumnIndex).Name = "colColValueRange" Then
If e.Button = MouseButtons.Left Then
Dim frmPick As New frmValueCheck
If cell.Value IsNot Nothing AndAlso cell.Value <> String.Empty Then
If cell.Value.ToString.IndexOf("#include") = 0 Then
frmPick.txtCode.Text = cell.Value
Else
frmPick.txtChar.Text = cell.Value
End If
End If
If frmPick.ShowDialog(Me) = DialogResult.OK Then
cell.Value = frmPick.DialogResultString
End If
End If
If e.Button = MouseButtons.Right Then
cell.Value = Nothing
End If
End If
End Sub
'增加行规则
Private Sub butRowRulaAdd_Click(sender As Object, e As EventArgs) Handles butRowRulaAdd.Click
Dim rw = dgvRowPula.Rows.Add()
dgvRowPula.Rows(rw).Cells("rowRowCountVariable").Value = String.Empty
dgvRowPula.Rows(rw).Cells("rowRowCountOffset").Value = 1
dgvRowPula.Rows(rw).Cells("rowRowRula").Value = String.Empty
End Sub
'删除行规则
Private Sub butRowRulaDel_Click(sender As Object, e As EventArgs) Handles butRowRulaDel.Click
If dgvRowPula.RowCount > 1 Then
dgvRowPula.Rows.RemoveAt(dgvRowPula.RowCount - 1)
End If
End Sub
#End Region
'获取首行定义,表示该段的首行信息
Private Sub butUpInDate_Click(sender As Object, e As EventArgs) Handles butUpInDate.Click
'==============================获取数据组、首行、变量定义===============================
'获取数据分段截止编号,这代表了该段应生成到几组数据
DataGroup.Clear()
For Each rw As DataGridViewRow In dgvDataGroup.Rows
DataGroup.Add(rw.Cells("colDataCount").Value)
Next
'获取变量预定义,这表示用到的变量对应的可见性
Dim HandPula As New Dictionary(Of String, Boolean)
For Each rw As DataGridViewRow In dgvHandPula.Rows
HandPula.Add(rw.Cells("colVariableName").Value, rw.Cells("colVisible").Value)
Next
MainRula.Clear()
For i As Integer = 0 To dgvMainRula.RowCount - 1
MainRula.Add(New List(Of MainRulaInfo))
Dim tmp As New MainRulaInfo
Dim group As Integer = dgvMainRula.Rows(i).Cells("colGroupID").Value
tmp.GroupCount = DataGroup(group - 1) '所属段需要生成几组数据
tmp.VariableName = dgvMainRula.Rows(i).Cells("colVariable").Value '变量名
tmp.Visible = HandPula(tmp.VariableName) '变量可见性
tmp.MinValue = dgvMainRula.Rows(i).Cells("colMinValue").Value '变量最小值
tmp.MaxValue = dgvMainRula.Rows(i).Cells("colMaxValue").Value '变量最大值
tmp.Absolute = dgvMainRula.Rows(i).Cells("colAbs").Value '取值范围是否是绝对值
MainRula(group - 1).Add(tmp)
Next
'=============================获取列生成信息===============================
ColRula.Clear()
'获取后续列信息
For i As Integer = 0 To dgvColPula.RowCount - 1
Dim tmp As New ColRulaInfo
tmp.ColCountVariable = dgvColPula.Rows(i).Cells("colColCountVariable").Value
tmp.ColCountOffset = dgvColPula.Rows(i).Cells("colColCountOffset").Value
tmp.ColType = dgvColPula.Rows(i).Cells("colColType").Value
tmp.ColVariableRula = dgvColPula.Rows(i).Cells("colColVariableRula").Value
If tmp.ColVariableRula = String.Empty Then
MsgBox("必须为每列指定范围限定变量,对于数字型将会把该变量最大最小值作为随机范围,对于字符型该变量决定该列生成多少个字符。", MsgBoxStyle.OkOnly)
dgvColPula.Rows(i).Selected = True
Return
End If
If tmp.ColType = "字符串" AndAlso (tmp.ColVariableRula Is Nothing OrElse tmp.ColVariableRula = String.Empty) Then
MsgBox("类型为字符串时,必须设定可用字符串或用代码检查字符是否合法", MsgBoxStyle.OkOnly)
dgvColPula.Rows(i).Selected = True
Return
End If
If tmp.ColType = "字符串" AndAlso tmp.ColCountVariable = String.Empty Then
If MsgBox("列基准决定了生成多少列字符串,如果需要生成变量预定义中的变量个数个字符串,则应以该变量为列数基准,最终生成的列数为列数基准+列数偏移。" & vbCrLf & "点击确定,按列数偏移数据生成" & tmp.ColCountOffset & "列字符", MsgBoxStyle.YesNo) = MsgBoxResult.No Then
dgvColPula.Rows(i).Selected = True
Return
End If
End If
tmp.ColValueRange = dgvColPula.Rows(i).Cells("colColValueRange").Value
tmp.ColNoRepeatInRow = dgvColPula.Rows(i).Cells("colColNoRepeatInRow").Value
tmp.ColNoRepeatInCol = dgvColPula.Rows(i).Cells("colColNoRepeatInCol").Value
tmp.ColSpace = dgvColPula.Rows(i).Cells("colColSpace").Value
ColRula.Add(tmp)
Next
'=============================获取行生成信息===============================
'获取后续行信息
RowRula.Clear()
For i As Integer = 0 To dgvRowPula.RowCount - 1
Dim tmp As New RowRulaInfo
tmp.RowCountVariable = dgvRowPula.Rows(i).Cells("rowRowCountVariable").Value
tmp.RowCountOffset = dgvRowPula.Rows(i).Cells("rowRowCountOffset").Value
tmp.RowRula = dgvRowPula.Rows(i).Cells("rowRowRula").Value
tmp.RowRulaArg1 = dgvRowPula.Rows(i).Cells("rowRowRulaArg1").Value
tmp.RowRulaArg2 = dgvRowPula.Rows(i).Cells("rowRowRulaArg2").Value
RowRula.Add(tmp)
Next
Me.DialogResult = DialogResult.OK
Me.Hide()
End Sub
Private Sub dgvColPula_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgvColPula.CellContentClick
End Sub
End Class
三、根据规则生成数据
vbnet
#Region "生成输入数据"
Sub MathInData()
Dim grpcnt As Integer = 0
Dim grpMainRula As List(Of List(Of frmRulaInData.MainRulaInfo))
'遍历数据段
For grp As Integer = 0 To frmRulaInData.DataGroup.Count - 1
'0、重新生成该段使用的预定义变量规则,这样就可以把必须出现的特殊样例规则先计算出来
grpMainRula = New List(Of List(Of frmRulaInData.MainRulaInfo))
If Not MathGroupRulas(frmRulaInData.MainRula(grp), grpMainRula) Then Return
'1、生成该段应有的数据组数
grpcnt = 0
While grpcnt < frmRulaInData.DataGroup(grp)
'1.1、按段规则生成一组数据
Dim curIn As New List(Of List(Of InDataCell))
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在生成首行数据......"
My.Application.DoEvents()
'1.1.1生成首行数据
MathHeadLine(grpMainRula(grpcnt), curIn)
'1.1.2生成后续数据
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在生成后续数据......"
My.Application.DoEvents()
If Not MathRula(grpMainRula(grpcnt), curIn) Then Return
'1.2、检测每组数据的输出是否符合输出规则
Dim curOut As New List(Of String)
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在检测生成数据的合法性......"
My.Application.DoEvents()
Dim exitFlag As Boolean
If CheckOutValue(curIn, curOut, exitFlag) Then
'记录合格的输入和输出
InData.Add(curIn)
OutData.Add(curOut)
grpcnt += 1
End If
If exitFlag Then Return
End While
Next
End Sub
'把当前的规则进行展开,得到特定数据检测规则。
Protected Friend Function MathGroupRulas(oldRula As List(Of frmRulaInData.MainRulaInfo), newRulas As List(Of List(Of frmRulaInData.MainRulaInfo))) As Boolean
MathGroupRulas(0, oldRula, New List(Of frmRulaInData.MainRulaInfo), newRulas)
If newRulas.Count > oldRula(0).GroupCount Then
If MsgBox("特定输入生成的数据组数比该组容纳的数据要多," & vbCrLf & "点击是忽略问题,点击否重新设置。", MsgBoxStyle.YesNo) = MsgBoxResult.No Then Return False
End If
'用原来的规则补足非特定数据检测规则------从前面插入避免特殊数据出现在第一组,太打击学生了。
For i As Integer = newRulas.Count To oldRula(0).GroupCount - 1
newRulas.Insert(0, oldRula)
Next
Return True
End Function
''' <summary>
''' 递归得到规则展开
''' </summary>
''' <param name="oldid">第几个变量</param>
''' <param name="oldRula">原来的规则</param>
''' <param name="newRula">当前正在展开的规则</param>
''' <param name="newRulas">展开之后的全部规则</param>
Protected Friend Sub MathGroupRulas(oldid As Integer, oldRula As List(Of frmRulaInData.MainRulaInfo), newRula As List(Of frmRulaInData.MainRulaInfo), newRulas As List(Of List(Of frmRulaInData.MainRulaInfo)))
If oldid = oldRula.Count Then '没有后续了,添加到结果
newRulas.Add(newRula)
Else
Dim flagMin As String = oldRula(oldid).MinValue.Split("|")(2)
Dim flagMax As String = oldRula(oldid).MaxValue.Split("|")(2)
If flagMin = 0 AndAlso flagMax = 0 Then
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
curRula.Add(oldRula(oldid))
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
Else
If flagMin = 1 Then '最小值需要形成特例,只需把最大值修改为最小值,随机时将只能得到最小值
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
Dim nr As frmRulaInData.MainRulaInfo = oldRula(oldid)
nr.MaxValue = nr.MinValue
curRula.Add(nr)
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
End If
If flagMax = 1 Then '最大值需要形成特例
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
Dim nr As frmRulaInData.MainRulaInfo = oldRula(oldid)
nr.MinValue = nr.MaxValue
curRula.Add(nr)
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
End If
End If
End If
End Sub
'按段生成首行数据(该行数据来自数据定义,无特殊操作)
Protected Friend Sub MathHeadLine(CurDataRula As List(Of frmRulaInData.MainRulaInfo), ByRef lst As List(Of List(Of InDataCell)))
'遍历首行数据预定义,根据可见性生成首航数据
Dim rula As frmRulaInData.MainRulaInfo
Dim line As New List(Of InDataCell)
'计算新的规则变量的初始值
For vid As Integer = 0 To CurDataRula.Count - 1
rula = CurDataRula(vid)
curRulaValue(rula.VariableName) = RndByRula(rula).ToString("F0")
rula.lastValue(rula.VariableName) = curRulaValue(rula.VariableName)
If rula.Visible Then
line.Add(New InDataCell(curRulaValue(rula.VariableName), 1))
End If
Next
If line.Count > 0 Then line(line.Count - 1) = New InDataCell(line(line.Count - 1).Value, 0) '去掉首行结尾空格
lst.Add(line) '添加首行数据(可能是空的,输出时输出),因为判断同列不重复时从1开始,所以即使没有首行也要占位
End Sub
'按变量规则随机一个确定值
Private Function RndByRula(ByRef rula As frmRulaInData.MainRulaInfo) As Decimal
Dim min, max As Decimal
MathMinMax(rula, min, max)
Return ((max - min) * rnd.NextDouble() + min)
End Function
'按段根据列规则生成一行后续行数据
Protected Friend Function MathLines(CurDataRula As List(Of frmRulaInData.MainRulaInfo), lst As List(Of List(Of InDataCell))) As Boolean
Dim rula As frmRulaInData.MainRulaInfo
Dim colCntCur As Integer
Dim varName As String
Dim line As New List(Of InDataCell)
'遍历列规则
For c As Integer = 0 To frmRulaInData.ColRula.Count - 1
'获取规则下生成的列数
colCntCur = frmRulaInData.ColRula(c).ColCountOffset
varName = frmRulaInData.ColRula(c).ColCountVariable
If varName <> String.Empty Then
colCntCur += curRulaValue(varName)
End If
'生成数据:类型、依据变量、有效值、空格
varName = frmRulaInData.ColRula(c).ColVariableRula
rula = CurDataRula.Find(Function(n) n.VariableName = varName)
'获取数值合法性程序
Dim cPipe As Pipe = Nothing
Dim k = frmRulaInData.ColRula(c)
Dim code As String = frmRulaInData.ColRula(c).ColValueRange
If code IsNot Nothing AndAlso code <> String.Empty AndAlso code.IndexOf("#include") = 0 Then
'编译
lblShowSub.Text = "编译数值合法性检查程序......"
My.Application.DoEvents()
If Not Compile(code, c, frmMain.Label3.Text) Then
Dim errlst As String = String.Empty
For Each errline In cErrLine
errlst &= errline
Next
MsgBox(errlst, MsgBoxStyle.OkOnly, "数值检测程序编译错误")
Return False
End If
gPipe = Nothing
'运行程序
lblShowSub.Text = "启动数值合法性检查程序......"
My.Application.DoEvents()
cPipe = RunExe(c)
End If
'生成指定列数据
lblShowSub.Text = "生成第" & 1 & "列数据......"
Dim n1flag As Boolean = False
For i As Integer = 1 To colCntCur
If i Mod 100 = 0 Then
lblShowSub.Text = "生成第" & i & "列数据......"
My.Application.DoEvents()
End If
Dim newIndataCell As New InDataCell
Do
'根据不同类型进行不同的生成,并检查了行列不重复
Select Case frmRulaInData.ColRula(c).ColType
Case "整数"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F0")
Case "小数.1"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F1")
Case "小数.2"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F2")
Case "小数.3"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F3")
Case "小数.4"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F4")
Case "小数.10"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F10")
Case "小数.15"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F15")
Case "字符串"
newIndataCell.Value = MahtStringByRual(c, rula, cPipe, code)
Case "[0,范围依据)不重复整数" '该功能仅针对第一个遇到的规则生效。
'列设置1,会生成范围依据内的一系列带空格的。
Dim Seq As List(Of Decimal)
Dim useIndex As Integer = 0
Seq = New List(Of Decimal)
For vi As Decimal = 0 To rula.lastValue(rula.VariableName) - 1
Seq.Add(vi)
Next
For vid As Integer = 0 To Seq.Count - 1
Dim tmp As Decimal = Seq(vid)
Dim rid As Integer = rnd.Next(0, Seq.Count)
Seq(vid) = Seq(rid)
Seq(rid) = tmp
Next
Dim seqBuff = New Text.StringBuilder
For vid As Integer = 0 To Seq.Count - 1
seqBuff.Append(Seq(vid))
seqBuff.Append(" ")
Next
seqBuff.Remove(seqBuff.Length - 1, 1)
newIndataCell.Value = seqBuff.ToString
Case "[1,范围依据]不重复整数" '该功能仅针对第一个遇到的规则生效。
'列设置1,会生成范围依据内的一系列带空格的。
Dim Seq As List(Of Decimal)
Dim useIndex As Integer = 0
Seq = New List(Of Decimal)
For vi As Decimal = 1 To rula.lastValue(rula.VariableName)
Seq.Add(vi)
Next
For vid As Integer = 0 To Seq.Count - 1
Dim tmp As Decimal = Seq(vid)
Dim rid As Integer = rnd.Next(0, Seq.Count)
Seq(vid) = Seq(rid)
Seq(rid) = tmp
Next
Dim seqBuff = New Text.StringBuilder
For vid As Integer = 0 To Seq.Count - 1
seqBuff.Append(Seq(vid))
seqBuff.Append(" ")
Next
seqBuff.Remove(seqBuff.Length - 1, 1)
newIndataCell.Value = seqBuff.ToString
End Select
Loop Until CheckInValueByRow(frmRulaInData.ColRula(c).ColNoRepeatInRow, line, newIndataCell.Value) AndAlso CheckInValueByCol(c, lst, newIndataCell.Value)
If frmRulaInData.ColRula(c).ColSpace > 0 Then
newIndataCell.SpaceCount = rnd.Next(1, frmRulaInData.ColRula(c).ColSpace + 1)
ElseIf frmRulaInData.ColRula(c).ColSpace < 0 Then
If frmRulaInData.ColRula(c).ColSpace = -1 Then
newIndataCell.SpaceCount = 1
n1flag = True
Else
newIndataCell.SpaceCount = frmRulaInData.ColRula(c).ColSpace
End If
End If
line.Add(newIndataCell)
Next
If n1flag Then line.Add(New InDataCell(String.Empty, -1))
'关闭数值合法性程序
If cPipe IsNot Nothing Then
cPipe.ExitProcess()
End If
Next
lst.Add(line) '添加一行数据
Return True
End Function
'将规则中的最大最小值解释为实际数值
Private Sub MathMinMax(rula As frmRulaInData.MainRulaInfo, ByRef rulamin As Decimal, ByRef rulamax As Decimal)
Dim minst() As String = rula.MinValue.Split("|")
Dim maxst() As String = rula.MaxValue.Split("|")
If minst(0) = String.Empty Then '无变量
rulamin = minst(1)
Else '有变量
rulamin = curRulaValue(minst(0)) + minst(1)
End If
If maxst(0) = String.Empty Then '无变量
rulamax = maxst(1)
Else '有变量,取变量当前值与常量计算结果
rulamax = curRulaValue(maxst(0)) + maxst(1)
End If
If rula.Absolute Then
Dim flag As Integer = rnd.Next(0, 2) '0,1两种情况
If flag = 0 Then '0作为负值的情况
rulamin = -rulamax
rulamax = -rulamin
End If
End If
If rulamin > rulamax Then
Dim tmp = rulamin
rulamin = rulamax
rulamax = tmp
End If
End Sub
我们只需要按照定义的规则,编写每个规则的解释器就可以了。
四、自动编译C++代码和运行
我们使用DevC++的编译工具即可,在VB.NET中可以用管道来运行:
vbnet
#Region "生成输入数据"
Sub MathInData()
Dim grpcnt As Integer = 0
Dim grpMainRula As List(Of List(Of frmRulaInData.MainRulaInfo))
'遍历数据段
For grp As Integer = 0 To frmRulaInData.DataGroup.Count - 1
'0、重新生成该段使用的预定义变量规则,这样就可以把必须出现的特殊样例规则先计算出来
grpMainRula = New List(Of List(Of frmRulaInData.MainRulaInfo))
If Not MathGroupRulas(frmRulaInData.MainRula(grp), grpMainRula) Then Return
'1、生成该段应有的数据组数
grpcnt = 0
While grpcnt < frmRulaInData.DataGroup(grp)
'1.1、按段规则生成一组数据
Dim curIn As New List(Of List(Of InDataCell))
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在生成首行数据......"
My.Application.DoEvents()
'1.1.1生成首行数据
MathHeadLine(grpMainRula(grpcnt), curIn)
'1.1.2生成后续数据
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在生成后续数据......"
My.Application.DoEvents()
If Not MathRula(grpMainRula(grpcnt), curIn) Then Return
'1.2、检测每组数据的输出是否符合输出规则
Dim curOut As New List(Of String)
lblInfo.Text = "生成第 " & grp + 1 & " 段数据的第:" & grpcnt + 1 & " 组数据。" & vbCrLf & "正在检测生成数据的合法性......"
My.Application.DoEvents()
Dim exitFlag As Boolean
If CheckOutValue(curIn, curOut, exitFlag) Then
'记录合格的输入和输出
InData.Add(curIn)
OutData.Add(curOut)
grpcnt += 1
End If
If exitFlag Then Return
End While
Next
End Sub
'把当前的规则进行展开,得到特定数据检测规则。
Protected Friend Function MathGroupRulas(oldRula As List(Of frmRulaInData.MainRulaInfo), newRulas As List(Of List(Of frmRulaInData.MainRulaInfo))) As Boolean
MathGroupRulas(0, oldRula, New List(Of frmRulaInData.MainRulaInfo), newRulas)
If newRulas.Count > oldRula(0).GroupCount Then
If MsgBox("特定输入生成的数据组数比该组容纳的数据要多," & vbCrLf & "点击是忽略问题,点击否重新设置。", MsgBoxStyle.YesNo) = MsgBoxResult.No Then Return False
End If
'用原来的规则补足非特定数据检测规则------从前面插入避免特殊数据出现在第一组,太打击学生了。
For i As Integer = newRulas.Count To oldRula(0).GroupCount - 1
newRulas.Insert(0, oldRula)
Next
Return True
End Function
''' <summary>
''' 递归得到规则展开
''' </summary>
''' <param name="oldid">第几个变量</param>
''' <param name="oldRula">原来的规则</param>
''' <param name="newRula">当前正在展开的规则</param>
''' <param name="newRulas">展开之后的全部规则</param>
Protected Friend Sub MathGroupRulas(oldid As Integer, oldRula As List(Of frmRulaInData.MainRulaInfo), newRula As List(Of frmRulaInData.MainRulaInfo), newRulas As List(Of List(Of frmRulaInData.MainRulaInfo)))
If oldid = oldRula.Count Then '没有后续了,添加到结果
newRulas.Add(newRula)
Else
Dim flagMin As String = oldRula(oldid).MinValue.Split("|")(2)
Dim flagMax As String = oldRula(oldid).MaxValue.Split("|")(2)
If flagMin = 0 AndAlso flagMax = 0 Then
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
curRula.Add(oldRula(oldid))
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
Else
If flagMin = 1 Then '最小值需要形成特例,只需把最大值修改为最小值,随机时将只能得到最小值
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
Dim nr As frmRulaInData.MainRulaInfo = oldRula(oldid)
nr.MaxValue = nr.MinValue
curRula.Add(nr)
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
End If
If flagMax = 1 Then '最大值需要形成特例
Dim curRula As New List(Of frmRulaInData.MainRulaInfo)(newRula)
Dim nr As frmRulaInData.MainRulaInfo = oldRula(oldid)
nr.MinValue = nr.MaxValue
curRula.Add(nr)
MathGroupRulas(oldid + 1, oldRula, curRula, newRulas)
End If
End If
End If
End Sub
'按段生成首行数据(该行数据来自数据定义,无特殊操作)
Protected Friend Sub MathHeadLine(CurDataRula As List(Of frmRulaInData.MainRulaInfo), ByRef lst As List(Of List(Of InDataCell)))
'遍历首行数据预定义,根据可见性生成首航数据
Dim rula As frmRulaInData.MainRulaInfo
Dim line As New List(Of InDataCell)
'计算新的规则变量的初始值
For vid As Integer = 0 To CurDataRula.Count - 1
rula = CurDataRula(vid)
curRulaValue(rula.VariableName) = RndByRula(rula).ToString("F0")
rula.lastValue(rula.VariableName) = curRulaValue(rula.VariableName)
If rula.Visible Then
line.Add(New InDataCell(curRulaValue(rula.VariableName), 1))
End If
Next
If line.Count > 0 Then line(line.Count - 1) = New InDataCell(line(line.Count - 1).Value, 0) '去掉首行结尾空格
lst.Add(line) '添加首行数据(可能是空的,输出时输出),因为判断同列不重复时从1开始,所以即使没有首行也要占位
End Sub
'按变量规则随机一个确定值
Private Function RndByRula(ByRef rula As frmRulaInData.MainRulaInfo) As Decimal
Dim min, max As Decimal
MathMinMax(rula, min, max)
Return ((max - min) * rnd.NextDouble() + min)
End Function
'按段根据列规则生成一行后续行数据
Protected Friend Function MathLines(CurDataRula As List(Of frmRulaInData.MainRulaInfo), lst As List(Of List(Of InDataCell))) As Boolean
Dim rula As frmRulaInData.MainRulaInfo
Dim colCntCur As Integer
Dim varName As String
Dim line As New List(Of InDataCell)
'遍历列规则
For c As Integer = 0 To frmRulaInData.ColRula.Count - 1
'获取规则下生成的列数
colCntCur = frmRulaInData.ColRula(c).ColCountOffset
varName = frmRulaInData.ColRula(c).ColCountVariable
If varName <> String.Empty Then
colCntCur += curRulaValue(varName)
End If
'生成数据:类型、依据变量、有效值、空格
varName = frmRulaInData.ColRula(c).ColVariableRula
rula = CurDataRula.Find(Function(n) n.VariableName = varName)
'获取数值合法性程序
Dim cPipe As Pipe = Nothing
Dim k = frmRulaInData.ColRula(c)
Dim code As String = frmRulaInData.ColRula(c).ColValueRange
If code IsNot Nothing AndAlso code <> String.Empty AndAlso code.IndexOf("#include") = 0 Then
'编译
lblShowSub.Text = "编译数值合法性检查程序......"
My.Application.DoEvents()
If Not Compile(code, c, frmMain.Label3.Text) Then
Dim errlst As String = String.Empty
For Each errline In cErrLine
errlst &= errline
Next
MsgBox(errlst, MsgBoxStyle.OkOnly, "数值检测程序编译错误")
Return False
End If
gPipe = Nothing
'运行程序
lblShowSub.Text = "启动数值合法性检查程序......"
My.Application.DoEvents()
cPipe = RunExe(c)
End If
'生成指定列数据
lblShowSub.Text = "生成第" & 1 & "列数据......"
Dim n1flag As Boolean = False
For i As Integer = 1 To colCntCur
If i Mod 100 = 0 Then
lblShowSub.Text = "生成第" & i & "列数据......"
My.Application.DoEvents()
End If
Dim newIndataCell As New InDataCell
Do
'根据不同类型进行不同的生成,并检查了行列不重复
Select Case frmRulaInData.ColRula(c).ColType
Case "整数"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F0")
Case "小数.1"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F1")
Case "小数.2"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F2")
Case "小数.3"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F3")
Case "小数.4"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F4")
Case "小数.10"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F10")
Case "小数.15"
newIndataCell.Value = MahtDecimalByRual(c, rula, cPipe, "F15")
Case "字符串"
newIndataCell.Value = MahtStringByRual(c, rula, cPipe, code)
Case "[0,范围依据)不重复整数" '该功能仅针对第一个遇到的规则生效。
'列设置1,会生成范围依据内的一系列带空格的。
Dim Seq As List(Of Decimal)
Dim useIndex As Integer = 0
Seq = New List(Of Decimal)
For vi As Decimal = 0 To rula.lastValue(rula.VariableName) - 1
Seq.Add(vi)
Next
For vid As Integer = 0 To Seq.Count - 1
Dim tmp As Decimal = Seq(vid)
Dim rid As Integer = rnd.Next(0, Seq.Count)
Seq(vid) = Seq(rid)
Seq(rid) = tmp
Next
Dim seqBuff = New Text.StringBuilder
For vid As Integer = 0 To Seq.Count - 1
seqBuff.Append(Seq(vid))
seqBuff.Append(" ")
Next
seqBuff.Remove(seqBuff.Length - 1, 1)
newIndataCell.Value = seqBuff.ToString
Case "[1,范围依据]不重复整数" '该功能仅针对第一个遇到的规则生效。
'列设置1,会生成范围依据内的一系列带空格的。
Dim Seq As List(Of Decimal)
Dim useIndex As Integer = 0
Seq = New List(Of Decimal)
For vi As Decimal = 1 To rula.lastValue(rula.VariableName)
Seq.Add(vi)
Next
For vid As Integer = 0 To Seq.Count - 1
Dim tmp As Decimal = Seq(vid)
Dim rid As Integer = rnd.Next(0, Seq.Count)
Seq(vid) = Seq(rid)
Seq(rid) = tmp
Next
Dim seqBuff = New Text.StringBuilder
For vid As Integer = 0 To Seq.Count - 1
seqBuff.Append(Seq(vid))
seqBuff.Append(" ")
Next
seqBuff.Remove(seqBuff.Length - 1, 1)
newIndataCell.Value = seqBuff.ToString
End Select
Loop Until CheckInValueByRow(frmRulaInData.ColRula(c).ColNoRepeatInRow, line, newIndataCell.Value) AndAlso CheckInValueByCol(c, lst, newIndataCell.Value)
If frmRulaInData.ColRula(c).ColSpace > 0 Then
newIndataCell.SpaceCount = rnd.Next(1, frmRulaInData.ColRula(c).ColSpace + 1)
ElseIf frmRulaInData.ColRula(c).ColSpace < 0 Then
If frmRulaInData.ColRula(c).ColSpace = -1 Then
newIndataCell.SpaceCount = 1
n1flag = True
Else
newIndataCell.SpaceCount = frmRulaInData.ColRula(c).ColSpace
End If
End If
line.Add(newIndataCell)
Next
If n1flag Then line.Add(New InDataCell(String.Empty, -1))
'关闭数值合法性程序
If cPipe IsNot Nothing Then
cPipe.ExitProcess()
End If
Next
lst.Add(line) '添加一行数据
Return True
End Function
'将规则中的最大最小值解释为实际数值
Private Sub MathMinMax(rula As frmRulaInData.MainRulaInfo, ByRef rulamin As Decimal, ByRef rulamax As Decimal)
Dim minst() As String = rula.MinValue.Split("|")
Dim maxst() As String = rula.MaxValue.Split("|")
If minst(0) = String.Empty Then '无变量
rulamin = minst(1)
Else '有变量
rulamin = curRulaValue(minst(0)) + minst(1)
End If
If maxst(0) = String.Empty Then '无变量
rulamax = maxst(1)
Else '有变量,取变量当前值与常量计算结果
rulamax = curRulaValue(maxst(0)) + maxst(1)
End If
If rula.Absolute Then
Dim flag As Integer = rnd.Next(0, 2) '0,1两种情况
If flag = 0 Then '0作为负值的情况
rulamin = -rulamax
rulamax = -rulamin
End If
End If
If rulamin > rulamax Then
Dim tmp = rulamin
rulamin = rulamax
rulamax = tmp
End If
End Sub
使用上述类,即可编译和运行C++代码:
vbnet
#Region "编译CPP文件、运行exe文件"
Protected Friend Function Compile(code As String, id As String, ca As String) As Boolean
Try
cPipeLine.Clear()
cErrLine.Clear()
cWarnLine.Clear()
Dim filecpp As String = tmppath & "\" & id & ".cpp"
Dim myCode As String = code.Replace("【", String.Empty).Replace("】", String.Empty)
If Not IO.Directory.Exists(tmppath) Then
IO.Directory.CreateDirectory(tmppath)
End If
IO.File.WriteAllText(filecpp, myCode, System.Text.Encoding.UTF8) 'decc++支持ansi编码
Dim fileexe As String = tmppath & "\" & id & ".exe"
Dim oldproc = Process.GetProcessesByName(id)
If oldproc IsNot Nothing AndAlso oldproc.Length > 0 Then
For Each old In oldproc
old.Kill()
old.WaitForExit()
Next
End If
If IO.File.Exists(fileexe) Then IO.File.Delete(fileexe)
Dim gpath As String = IO.Path.GetFullPath(frmMain.txtMinGWPath.Text)
gPipe = New Pipe(gpath, "\g++.exe",
New Pipe.DelegateOnDataReceived(AddressOf CompileRunReadLine),
New Pipe.DelegateOnErrorReceived(AddressOf CompileRunErrorLine),
New Pipe.DelegateOnExitProcess(AddressOf ProcessExit),
filecpp & " -o " & fileexe & " " & frmMain.Label3.Text)
gPipe.WaitForExit()
Return cErrLine.Count = 0
Catch ex As Exception
MessageBox.Show(ex.ToString, "Compile失败!", MessageBoxButtons.OK)
Return False
End Try
End Function
Protected Friend Function RunExe(id As String) As Pipe
'运行并返回管道
cPipeLine.Clear()
cErrLine.Clear()
cWarnLine.Clear()
Dim fileexe As String = "\" & id & ".exe"
Dim cPipe As Pipe = Nothing
cPipe = New Pipe(tmppath, fileexe, New Pipe.DelegateOnDataReceived(AddressOf RunReadLine),
New Pipe.DelegateOnErrorReceived(AddressOf RunErrorLine),
New Pipe.DelegateOnExitProcess(AddressOf ProcessExit), "")
Return cPipe
End Function
Sub CompileRunReadLine(ByVal Info As String)
If Not String.IsNullOrEmpty(Info) Then
' 判断是否是警告信息
If Info.Contains("warning:") Then
' 警告信息,可以记录但不视为编译错误
' 或者放入单独的警告集合中
cWarnLine.Add(Info)
Else
' 真正的错误信息
cErrLine.Add(Info)
End If
End If
End Sub
Sub CompileRunErrorLine(ByVal info As String)
If Not String.IsNullOrEmpty(info) Then
' 真正的错误信息
cErrLine.Add(info)
End If
End Sub
Sub RunReadLine(ByVal Info As String)
If Not String.IsNullOrWhiteSpace(Info) Then
cPipeLine.Add(Info)
End If
End Sub
Sub RunErrorLine(ByVal info As String)
If Not String.IsNullOrEmpty(info) Then
cErrLine.Add(info)
End If
End Sub
Sub ProcessExit()
End Sub
#End Region
非核心技术代码就不再赘述。你可以按照这个思路自己编写一个普通数据生成器。而图论生成器要稍微麻烦一点,完全随机是很难得到完全合法的数据的,可以从形成的形状特征来入手,这样能够较好的得到普遍数据,最好使用一些图形化控件来把生成的图显示出来进行检查:
