前置知识

sympy

1
2
3
4
5
6
7
8
9
10
11
12
# 设置自变量为x
x = sympy.Symbol('x')
# 解析表达式
f1 = sympy.sympify('1-sin(x)**2')
f2 = sympy.sympify('cos(x)**2')
# 判等, == 有时会出错
print(f1.equals(f2))
# 表达式求导
print(sympy.diff(f1, x))
print(sympy.diff(f2, x))
# 表达式单点求值
print( f1.evalf(subs={x:math.pi}) )

xeger

根据正则表达式随机生成字符串

1
2
from xeger import Xeger
target = Xeger().xeger("正则表达式")

subprocess

创建子进程。参考

  1. Popen → 创建一个新的进程执行命令,返回一个实例,进行后续操作
  • Popen.poll()

    用于检查子进程是否已经结束。

  • Popen.wait(timeout=None)

    在规定时间内等待进程结束,设置 timeout=None,则一直等待直到结束

    返回:returncode

  • Popen.communicate(input=None)

    • 与子进程进行交互。向stdin发送数据并关闭它。可选参数input指定发送到子进程的数据,注意 Popen对象的encoding或者text参数决定传入字符串还是字节流。
    • stdoutstderr中读取数据, 并关闭。如果没有数据发送到子进程(stdin),则返回一个元组:(stdoutdata, stderrdata)
    • 注:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdoutstderr获取数据,必须将stdoutstderr设置为PIPE
    1
    2
    3
    4
    5
    6
    7
    8
    subp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    encoding="utf-8")
    subp.communicate(data)
    if subp.poll() == 0:
    res = subp.communicate()[0]
    else:
    res = "失败"
    subp.kill()
  • Popen.kill()

    在 Posix 操作系统上,此函数给子进程发送 SIGKILL 信号。在 Windows 上, kill()terminate() 的别名。

递归下降语法分析

  • 形式化表述:
    • 表达式 → 空白项 [加减 空白项] 项 空白项 | 表达式 加减 空白项 项 空白项
    • 项 → [加减 空白项] 因子 | 项 空白项 * 空白项 因子
    • 因子 → 变量因子 | 常数因子 | 表达式因子
    • 变量因子 → 幂函数 | 三角函数
    • 常数因子 → 带符号的整数
    • 表达式因子 → ‘(‘ 表达式 ‘)’
    • 三角函数 → sin 空白项 ‘(‘ 空白项 因子 空白项 ‘)’ [空白项 指数] | cos 空白项 ‘(‘ 空白项 因子 空白项 ‘)’ [空白项 指数]
    • 幂函数 → x [空白项 指数]
    • 指数 → ** 空白项 带符号的整数
    • 带符号的整数 → [加减] 允许前导零的整数
    • 允许前导零的整数 → (0|1|2|…|9){0|1|2|…|9}
    • 空白字符 → (空格) | \t(水平制表符)
    • 空白项 → {空白字符}
    • 加减 → + | -
  • 构建与法分析树:

_  1.png

解析

数据构造

  • 常量池机制:

即提前设计好需要使用的常数,存在数组里面,每次需要随机生成常数的时候,随机的不是数而是数组下标。

  • 构造数据用途分类

sympy库的解析式中前导零被判定为不合法的,我们构造数据时可以生成相等的两组分别含与不含前导零的数据来用作测试和得到正确答案

使用

  • jar

step1:

准备MANIFEST.MF,并在其中写入如下内容

1
2
Manifest-Version: 1.0
Main-Class: MainClass

其中Main-Class后面是主类,后面需要加个回车,否则容易出奇怪的bug

step2:

编译.java文件,cd到那一堆.java文件夹里面,执行

1
jar cvfm MainClass.jar MANIFEST.MF *.class

step3:

把所有.class.MF放在一个文件夹里,终端执行

1
jar cvfm MainClass.jar MANIFEST.MF *.class

step4:

验证打包是否成功,可以终端执行

1
java -jar MainClass.jar

注意

有时IDEA打开别人的项目时,突然飘红,出现红色的波浪下划线,错误提示:cannot access com.xx......xx.class,无法访问同一个包下的类,根本原因是IDEA缓存出现问题,可以强制删除缓存。

File -> Invalidate Caches /Restart


评测姬:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import random
from xeger import Xeger
import sympy
import subprocess

DATE_FOR_SDTIN = 0
DATE_FOR_EVALUATION = 1

MANUAL = 0
MACHINE = 1


class DataGenerator():

def __init__(self):
self.FACTOR_MAX_PER_TERM = 6
self.TERM_MAX_PER_POLY = 10
self.constant_pool = [0, 1, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 2147483647,
5219260817, 5419260817, 9223372036854775807,
19260817192608172333, 19260817192608174666]

def addsub(self):
if random.randint(0, 1) & 1:
return "+"
else:
return "-"

def index(self):
index = self.signedInt()
blank = self.blankFactor()
return ["**" + blank + str(index[0]), "**" + blank + str(index[1])]

def blankFactor(self):
blank_factor = Xeger().xeger("[\t ]{0,4}")
return blank_factor

def signedInt(self):
if random.randint(0, 2) == 0:
char = ""
else:
char = self.addsub()
index = random.randint(0, len(self.constant_pool) - 1)
constant = self.constant_pool[index]
pre_zero = Xeger().xeger("0{0,4}")
return [char + str(pre_zero) + str(constant), char + str(constant)]

def powerFunction(self):
blank = self.blankFactor()
if random.randint(0, 1) == 0:
index = self.index()
index_list = [blank + index[0], blank + index[1]]
else:
index_list = ["", ""]
return ["x" + blank + index_list[0], "x" + blank + index_list[1]]

def triangleFactor(self):
blank = self.blankFactor()
index = self.index()
if random.randint(0, 1) == 0:
if random.randint(0, 1) == 0:
return ["sin" + blank + "(" + blank + "x" + blank + ")",
"sin" + blank + "(" + blank + "x" + blank + ")"]
else:
return ["sin" + blank + "(" + blank + "x" + blank + ")" + blank + index[0],
"sin" + blank + "(" + blank + "x" + blank + ")" + blank + index[1]]
else:
if random.randint(0, 1) == 0:
return ["cos" + blank + "(" + blank + "x" + blank + ")",
"cos" + blank + "(" + blank + "x" + blank + ")"]
else:
return ["cos" + blank + "(" + blank + "x" + blank + ")" + blank + index[0],
"cos" + blank + "(" + blank + "x" + blank + ")" + blank + index[1]]

def expFactor(self):
exp = self.getPolynome()
return ["(" + exp[0] + ")", "(" + exp[1] + ")"]

def getFactor(self):
select = random.randint(0, 3)
if select == 0:
factor = self.powerFunction()
elif select == 1:
factor = self.signedInt()
else:
factor = self.triangleFactor()
return factor

def getTerm(self, term_depth=1):
if term_depth > self.FACTOR_MAX_PER_TERM:
pre_char = Xeger().xeger("[" + self.addsub() + self.blankFactor() + "]?")
factor = self.getFactor()
return [pre_char + factor[0], pre_char + factor[1]]
term = self.getTerm(term_depth=term_depth + 1)
multiply_char = self.blankFactor() + "*" + self.blankFactor()
factor = self.getFactor()
return [term[0] + multiply_char + factor[0], term[1] + multiply_char + factor[1]]

def getPolynome(self, poly_depth=1):
if poly_depth > self.TERM_MAX_PER_POLY:
pre_char = self.blankFactor() + Xeger().xeger("[" + self.addsub() + self.blankFactor() + "]?")
term = self.getTerm()
blank = self.blankFactor()
return [pre_char + term[0] + blank, pre_char + term[1] + blank]
poly = self.getPolynome(poly_depth + 1)
pre_char = self.addsub() + self.blankFactor()
blank = self.blankFactor()
term = self.getTerm()
return [poly[0] + pre_char + term[0] + blank, poly[1] + pre_char + term[1] + blank]

def test(self):
print(self.getPolynome()[0])


def getPolyDiff(poly):
x = sympy.Symbol('x')
f = sympy.sympify(poly)
return sympy.diff(f, x)


def cmd(command, data):
subp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
encoding="utf-8")
subp.communicate(data)
if subp.poll() == 0:
res = subp.communicate()[0]
else:
res = "失败"
subp.kill()
return res


def judgeSingleDate(judge_object, mode, manual_data=""):
if mode == MACHINE:
poly_list = DataGenerator().getPolynome()
poly_for_evaluation = poly_list[DATE_FOR_EVALUATION]
poly_for_test = poly_list[DATE_FOR_SDTIN]
print("data: ", poly_for_evaluation)
print("data_for_test: ", poly_for_test)
diff_ans = getPolyDiff(poly_for_evaluation)
print("correct answer: ", diff_ans)
jar_ans = cmd("java -jar " + judge_object, poly_for_test)
print("your answer: ", jar_ans)
print("your identity: ", judge_object)
print("%s is right: " % judge_object, diff_ans.equals(jar_ans))
return diff_ans.equals(jar_ans)
if mode == MANUAL:
expData = manual_data
diff_ans = getPolyDiff(expData)
print("correct answer: ", diff_ans)
jar_ans = cmd("java -jar " + judge_object, expData)
print("your answer: ", jar_ans)
if jar_ans.strip() != "WRONG FORMAT!":
print("%s is right: " % judge_object, diff_ans.equals(jar_ans))
return diff_ans.equals(jar_ans)
return False


def judge(judge_list, mode, count=200):
index = 0
found = False
while index < count:
print("----------------------------------")
print("data ", index, " :")
error_name_list = []
if mode == MANUAL:
expData = input("your data: ")
for judge_object in judge_list:
right = judgeSingleDate(judge_object, mode=mode, manual_data=expData)
if not right:
found = True
error_name_list.append(judge_object)
if found:
print("ERROR!!!!!!!!!!!!!")
print("Error person: ", error_name_list)
return
index = index + 1
print("Congratulations! you all killed!")


if __name__ == "__main__":
judgelist = ["Alterego.jar", "Archer.jar", "Assassin.jar", "Berserker.jar", "Caster.jar", "Rider.jar",
"oo_hw_3.jar", "saber.jar"]
judge(judgelist, MANUAL)