<!doctype html>css
第15组:JL17110067 隆晋威 PB16120853 赵瑞html
https://github.com/NiceKingWei/homework2node
status | stages | 预估耗时 | 实际耗时 |
---|---|---|---|
Accepted | make plan | 20 min | 20 min |
Accepted | demand analysis | 40 min | 40 min |
Accepted | analysis | 45 min | 90 min |
Accepted | code | 3 hours | 5 hours |
Accepted | test | 2 hours | 3 hours |
Accepted | report | 1 hours | 2 hours |
Sum | 8 hours | 12 hours |
像《构建之法》的人物阿超那样,写一个能自动生成小学四则运算题目并给出答案的命令行 “软件”, 若是咱们要把这个功能放到不一样的环境中去(例如,命令行,Windows 图形界面程序,网页程序,手机App),就会碰到困难,由于目前代码的广泛问题是代码都散落在main ( )函数或者其余子函数中,咱们很难把这些功能完整地剥离出来,做为一个独立的模块知足不一样的需求。python
setting()
function:ios
x
1
void setting(
2
int max_opearators, //操做数的数目最大值
3
long max_range, //中间结果和结果的范围,若是是分数,只限制分母和最后结果的值
4
int precision, //精度
5
int has_fraction, //是否含有分数,1:含有,0:不含有分数
6
int has_real) //是否含有小数,1:含有,0:不含有1
7
{
8
9
if (max_opearators != -1) global_setting.max_opearators = max_opearators;
10
if (max_range != -1) global_setting.max_range = max_range;
11
if (precision != -1) global_setting.precision = precision;
12
if (has_fraction != -1) global_setting.has_fraction = has_fraction;
13
if (has_real != -1) global_setting.has_real = has_real;
14
global_setting.max_num = max_range / 10;
15
}
就是能够屡次改变设置,在传入参数的时候,max_opearators
、max_range
、precision
能够传入值或者-1,传值表明更改该设置,-1表明不变。has_fraction
、has_real
能够传入1,0,-1;1表明开启,0表明没有,-1表明不变。c++
generate
函数:git
xxxxxxxxxx
1
1
void generate(string* question, string* answer);
将参数传进去,运行后结果就存在了string* answer
里面。github
咱们首先定义了 fraction 类,这是分数类,用于符号计算。web
xxxxxxxxxx
10
1
class fraction {
2
public:
3
long numerator, denominator;
4
void reduction() ;
5
fraction operator + (const fraction x) const;
6
fraction operator - (const fraction& x) const;
7
fraction operator * (const fraction& x) const;
8
fraction operator / (const fraction& x) const;
9
fraction operator ^ (fraction x) const;
10
};
fraction 类里面重载了各个运算函数,并在每次计算结束以后经过类中的reduction()
函数把分数变为最简分数。编程
而后定义了一些工具函数,如输出函数,判断是不是无效值的函数 is_bad_value
xxxxxxxxxx
2
1
bool is_bad_value(const fraction& x);
2
bool is_bad_value(const double& x);
is_bad_value
是一个判断值是不是坏值的函数。坏值在除零异常时可能会出现,在值超出范围的时候也会出现。坏值具备传递性,坏值和任何值运算的结果都是坏值。这种设计方式的灵感来自函数式语言中的 Maybe Monad
,下面放一下fraction operator / (const fraction x) const;
做为例子:
xxxxxxxxxx
17
1
fraction operator / (const fraction& x) const {
2
if (is_bad_value(*this))return *this;
3
if (is_bad_value(x))return x;
4
fraction stan_bad_value(1, 0);
5
if (x.numerator == 0) {
6
return stan_bad_value;
7
}
8
fraction result;
9
result.numerator = this->numerator * x.denominator;
10
result.denominator = this->denominator * x.numerator;
11
result.reduction();
12
if (is_bad_value(result)) {
13
result.numerator = 1;
14
result.denominator = 0;
15
}
16
return result;
17
}
接下来定义抽象语法树的结构。
xxxxxxxxxx
30
1
enum ASTNodeType { TYPE_ADD = 0, TYPE_MINUS = 1, TYPE_MUL = 2, TYPE_DIV = 3, TYPE_POWER = 4, TYPE_FRACTION = 5, TYPE_DOUBLE = 6 };
2
3
struct ASTNode;
4
5
union NodeData {
6
fraction frac;
7
double real;
8
pair<ASTNode*, ASTNode*> node;
9
10
NodeData() {
11
real = 0;
12
}
13
};
14
15
struct ASTNode {
16
ASTNodeType type;
17
NodeData data;
18
19
ASTNode() {
20
type = TYPE_DOUBLE;
21
}
22
23
~ASTNode() {
24
if (type != TYPE_FRACTION && type != TYPE_DOUBLE) {
25
delete data.node.first;
26
delete data.node.second;
27
}
28
}
29
};
30
每个抽象语法树的节点都包含两个字段,一个是 type,一个是 data。type 的类型是一个枚举值,data 是一个 union 联合体。这种作法的灵感也来自函数式语言。在表示抽象语法树时,tagged union
是一种很好的方式。但由于不肯定能不能使用 c++17,因此咱们并无用类型安全的 std::variant
,而是使用了本身定义的tagged union
。
以后的事情就比较简单了
xxxxxxxxxx
2
1
ASTNode* random_value(cal_mode mode);
2
ASTNode* random_ast(cal_mode mode);
这两个函数产生随机值和随机抽象语法树,根据 setting 的规则产生合适的表达式树。
这两个函数的代码:
xxxxxxxxxx
64
1
inline ASTNode* random_value(cal_mode mode) {
2
ASTNode* node = new ASTNode();
3
int m, n;
4
switch (mode) {
5
case MODE_FRACTION:
6
node->type = TYPE_FRACTION;
7
m = rand() % (global_setting.max_num - 1) + 1;
8
n = rand() % (m * 5);
9
if (global_setting.has_fraction) {
10
node->data.frac = fraction(n, m);
11
}
12
else {
13
node->data.frac = fraction(m);
14
}
15
break;
16
17
case MODE_REAL:
18
node->type = TYPE_DOUBLE;
19
double base = pow(10, global_setting.precision);
20
node->data.real = floor((rand() / (double)RAND_MAX)*global_setting.max_num*base) / base;
21
break;
22
}
23
return node;
24
}
25
26
ASTNode* random_ast(cal_mode mode) {
27
int n = global_setting.max_opearators <= 1 ? 1 : rand() % (global_setting.max_opearators - 1) + 1;
28
ASTNode* num1 = random_value(mode);
29
for (int i = 0; i<n; i++) {
30
ASTNode* num2 = random_value(mode);
31
if (rand() % 2) swap(num1, num2);
32
int r = rand() % 17;
33
ASTNode* new_node = new ASTNode();
34
35
36
if (r-- == 16 && (num2->type == TYPE_FRACTION || num2->type == TYPE_FRACTION) && (num1->type != TYPE_POWER) && global_setting.has_power) {
37
if (mode == MODE_FRACTION) num2->data.frac = fraction(rand() % 4 + 1);
38
else num2->data.real = rand() % 2 + 2;
39
40
new_node->type = TYPE_POWER;
41
new_node->data.node.first = num1;
42
new_node->data.node.second = num2;
43
}
44
else {
45
if (global_setting.has_mul_div) {
46
new_node->type = (ASTNodeType)(r / 4);
47
if (mode == MODE_FRACTION && !global_setting.has_fraction) {
48
r = rand() % 10;
49
if (r-- == 9) new_node->type = TYPE_DIV;
50
else new_node->type = (ASTNodeType)(r / 3);
51
}
52
}
53
else {
54
new_node->type = (ASTNodeType)(r / 8);
55
}
56
new_node->data.node.first = num1;
57
new_node->data.node.second = num2;
58
}
59
60
num1 = new_node;
61
}
62
return num1;
63
}
64
xxxxxxxxxx
2
1
ASTNode* calc_asttree(ASTNode* root);
2
ASTNode* ast_eval(ASTNode* root);
calc_asttree
是递归函数,它递归调用自身,计算左子树和右子树的值,而后再计算当前节点的值,若是有一个结点的type是TYPE_DOUBLE
,那么返回给上一层的type就是TYPE_DOUBLE
。
这两个函数的代码
xxxxxxxxxx
166
1
long long hash_value;
2
ASTNode* calc_asttree(ASTNode* root) {
3
ASTNode* result = new ASTNode();
4
result->type = TYPE_FRACTION;
5
result->data.frac;
6
ASTNode* temp_a = new ASTNode();
7
ASTNode* temp_b = new ASTNode();
8
switch (root->type) {
9
case TYPE_FRACTION:
10
result->type = TYPE_FRACTION;
11
result->data.frac = root->data.frac;
12
break;
13
case TYPE_DOUBLE:
14
result->type = TYPE_DOUBLE;
15
result->data.real = root->data.real;
16
break;
17
case TYPE_ADD:
18
temp_a = calc_asttree(root->data.node.first);
19
temp_b = calc_asttree(root->data.node.second);
20
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
21
result->type = TYPE_FRACTION;
22
result->data.frac = temp_a->data.frac + temp_b->data.frac;
23
}
24
else {
25
result->type = TYPE_DOUBLE;
26
double a, b;
27
if (temp_a->type == TYPE_FRACTION) {
28
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
29
}
30
else if (temp_a->type == TYPE_DOUBLE) {
31
a = temp_a->data.real;
32
}
33
if (temp_b->type == TYPE_FRACTION) {
34
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
35
}
36
else if (temp_b->type == TYPE_DOUBLE) {
37
b = temp_b->data.real;
38
}
39
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a + b);
40
}
41
break;
42
case TYPE_MINUS:
43
temp_a = calc_asttree(root->data.node.first);
44
temp_b = calc_asttree(root->data.node.second);
45
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
46
result->type = TYPE_FRACTION;
47
result->data.frac = temp_a->data.frac - temp_b->data.frac;
48
49
}
50
else {
51
result->type = TYPE_DOUBLE;
52
double a, b;
53
if (temp_a->type == TYPE_FRACTION) {
54
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
55
}
56
else if (temp_a->type == TYPE_DOUBLE) {
57
a = temp_a->data.real;
58
}
59
if (temp_b->type == TYPE_FRACTION) {
60
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
61
}
62
else if (temp_b->type == TYPE_DOUBLE) {
63
b = temp_b->data.real;
64
}
65
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a - b);
66
}
67
break;
68
case TYPE_MUL:
69
temp_a = calc_asttree(root->data.node.first);
70
temp_b = calc_asttree(root->data.node.second);
71
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
72
result->type = TYPE_FRACTION;
73
result->data.frac = temp_a->data.frac * temp_b->data.frac;
74
75
}
76
else {
77
result->type = TYPE_DOUBLE;
78
double a, b;
79
if (temp_a->type == TYPE_FRACTION) {
80
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
81
}
82
else if (temp_a->type == TYPE_DOUBLE) {
83
a = temp_a->data.real;
84
}
85
if (temp_b->type == TYPE_FRACTION) {
86
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
87
}
88
else if (temp_b->type == TYPE_DOUBLE) {
89
b = temp_b->data.real;
90
}
91
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a*b);
92
}
93
break;
94
case TYPE_DIV:
95
temp_a = calc_asttree(root->data.node.first);
96
temp_b = calc_asttree(root->data.node.second);
97
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
98
result->type = TYPE_FRACTION;
99
result->data.frac = temp_a->data.frac / temp_b->data.frac;
100
101
}
102
else {
103
result->type = TYPE_DOUBLE;
104
double a, b;
105
if (temp_a->type == TYPE_FRACTION) {
106
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
107
}
108
else if (temp_a->type == TYPE_DOUBLE) {
109
a = temp_a->data.real;
110
}
111
if (temp_b->type == TYPE_FRACTION) {
112
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
113
}
114
else if (temp_b->type == TYPE_DOUBLE) {
115
b = temp_b->data.real;
116
}
117
result->data.real = is_bad_value(a) || is_bad_value(b) || fabs(b) <= 1e-3 ? INFINITY : (a / b);
118
}
119
break;
120
case TYPE_POWER:
121
temp_a = calc_asttree(root->data.node.first);
122
temp_b = calc_asttree(root->data.node.second);
123
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
124
result->type = TYPE_FRACTION;
125
result->data.frac = temp_a->data.frac ^ temp_b->data.frac;
126
127
}
128
else {
129
result->type = TYPE_DOUBLE;
130
double a, b;
131
if (temp_a->type == TYPE_FRACTION) {
132
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
133
}
134
else if (temp_a->type == TYPE_DOUBLE) {
135
a = temp_a->data.real;
136
}
137
if (temp_b->type == TYPE_FRACTION) {
138
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
139
}
140
else if (temp_b->type == TYPE_DOUBLE) {
141
b = temp_b->data.real;
142
}
143
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : powl(a, b);
144
}
145
break;
146
}
147
long long value = (long long)(root->type == TYPE_FRACTION ? (root->data.frac.numerator / (double)root->data.frac.denominator) : root->data.real);
148
hash_value = (hash_value * 19260817 + value) % (long long)(1e9 + 7);
149
delete temp_a;
150
delete temp_b;
151
if (result->type == TYPE_FRACTION) {
152
if ( (result->data.frac.denominator > global_setting.max_range*100 || result->data.frac.numerator < 0) ||
153
(result->data.frac.denominator != 1 && !global_setting.has_fraction)) {
154
result->data.frac.numerator = 1;
155
result->data.frac.denominator = 0;
156
}
157
} else if (result->type == TYPE_DOUBLE && (result->data.real <0|| result->data.real>global_setting.max_range*10)) {
158
result->data.real = INFINITY;
159
}
160
return result;
161
}
162
163
ASTNode* ast_eval(ASTNode* root) {
164
hash_value = 0;
165
return calc_asttree(root);
166
}
ast_eval
是调用 calc_asttree
的函数,它先把 hash_code
设为0,而后调用 calc_asttree
在calc_asttree
递归计算的过程当中会产生一个操做序列,这个序列能够刻画当前表达式的特征,例如 1+2+3,在计算过程当中产生的序列是 1+2=3, 3+3 =6,3+(2+1) 在计算过程当中产生的序列为 2+1=3,3+3=6。若定义两个序列等价当前仅当序列中每一个算式在交换律意义下等价。能够发现,题目要求的 “重复” 条件与计算序列的等价条件是等价的。所以,咱们能够用计算序列来去重。
但计算序列的储存比较占空间,所以咱们选择了一个哈希函数,对计算序列进行哈希映射。由于两个序列哈希值相同是两个序列重复的必要条件。所以在实际操做中,只须要两个序列哈希函数不一样,则这两个表达式必然不相等。
接下来是表达式输出函数。
xxxxxxxxxx
13
1
/*
2
* Expr := AddExpr | Expr + AddExpr
3
* AddExpr := MulExpr | AddExpr * MulExpr
4
* MulExpr := PowExpr | MulExpr ^ PowExpr
5
* PowExpr := Number | (Expr)
6
*/
7
enum ExprType { EXPR_EXPR, EXPR_ADDEXPR, EXPR_MULEXPR, EXPR_POWEXPR };
8
9
void ast_output_expr(ASTNode* root, stringstream& ss);
10
void ast_output_addexpr(ASTNode* root, stringstream& ss);
11
void ast_output_mulexpr(ASTNode* root, stringstream& ss);
12
void ast_output_powexpr(ASTNode* root, stringstream& ss);
13
void ast_output_number(ASTNode* root, stringstream& ss);
注释中的内容是咱们的计算表达式的 BNF 范式。由语法产生式,咱们能够很容易地写出以上递归函数,并经过函数间的互相调用完成对表达式的输出,这种输出方式不会产生多余的括号。
xxxxxxxxxx
1
1
void generate(string* question, string* answer);
generate 函数生成题目和答案。它先调用 random_ast
生成一颗随机语法树,而后对它进行求值,若是结果是坏值,那就从新生成一个。
特别值得一提的是咱们的 main
函数
xxxxxxxxxx
18
1
int main() {
2
FILE* file = NULL;
3
const long long test_num = 100000;
4
const long long test_groups = 100;
5
for (long long i = 0; i<test_num; i++) {
6
if (i % (test_num / test_groups) == 0) {
7
stringstream ss;
8
ss << "test" << i / (test_num / test_groups) << ".py";
9
if (file) fclose(file);
10
file = fopen(ss.str().c_str(), "w");
11
}
12
string que, ans;
13
generate(&que, &ans);
14
fprintf(file, "assert(%lld>=0 and abs((%s)-(%s))<5e-2)\n", i, que.c_str(), ans.c_str());
15
}
16
fclose(file);
17
return 0;
18
}
它为每一组数据生成了一行 python 代码,是一句断言。只要断言成立,这组数据就是正确的。只须要简单改改更改参数,咱们就能够得到不少组数据,并能经过脚本进行自动测试。
xxxxxxxxxx
11
1
import os
2
import time
3
os.system("g++ core.cpp -O2 -g")
4
print("compile done.")
5
time.sleep(1);
6
os.system("./a.out")
7
time.sleep(1);
8
print("generate done.")
9
for i in range(0,100):
10
os.system("pypy test%d.py" % i)
11
print("test%d done." % i)
发布前,咱们组一共测试了 500万 组数据,均没有出错。
出现了错误的计算结果:
第二次出现了错误的结果:
最初,隆晋威同窗扮演驾驶员角色,赵瑞同窗扮演领航员角色。
在完成整个程序的框架后,分工完成模块细节和测试,互相作驾驶员和领航员,在整个程序写完后,一块儿测试这个程序,并debug。
一我的用git很随意,但是两我的的话,就要提早看一下队友的修改。
结对编程过程当中,两我的的做息一致性很重要,在刚写完程序的时候,不出意外出了错误结果,咱们开始debug,主要的debug任务是在凌晨完成的。debug到晚上十二点,1000组数据跑对了,后来加到10000又出错了,咱们又debug到1点,10000没问题的时候,当时已经凌晨两点了,若是两我的有一个不习惯熬夜的话,这还真有点不舒服了,还好我俩都很不养生。
两我的的debug的速度果真不是1+1=2的简单加法,速度比一我的debug要快得多。
还有就是一块儿工做在有进展的时候两我的会一块儿以为很开心,分享一下喜悦,比一我的有进展本身心里爽一下还happy,好比亮点debug完咱们觉得大功告成了就很开心(然而次日加大测试量又出现了新的错误样例)。
结对编程过程当中学习的速度时迅速的,这一次结对编程我向隆晋威同窗学到了不少,好比代码规范,GitHub的使用和visual studio code的使用,还有用脚原本测试程序。
会选择结对编程用于解决部分任务。
1
/*
2
* core.cpp
3
* author:
4
* PB16120853 赵瑞
5
* JL17110067 隆晋威
6
* date:
7
* 2018/4/5
8
*/
9
10
11
12
13
14
15
16
17
18
19
20
using namespace std;
21
22
/*
23
* global setting
24
*/
25
struct settings {
26
int max_opearators = 5;
27
long max_num = 100; // max_range / 10
28
long max_range = 1000;
29
int precision = 2;
30
bool has_fraction = true;
31
bool has_real = true;
32
bool has_mul_div = true;
33
bool has_power = true;
34
};
35
settings global_setting;
36
37
38
/*
39
* fraction
40
*/
41
class fraction;
42
bool is_bad_value(const fraction& x);
43
bool is_bad_value(const double& x);
44
45
46
47
class fraction {
48
private:
49
long gcd(long u, long v) {
50
if (!(u&&v)) {
51
return 1;
52
}
53
while (v != 0) {
54
long r = u % v;
55
u = v;
56
v = r;
57
}
58
return u;
59
}
60
public:
61
long numerator, denominator;
62
63
64
fraction(long m = 1, long n = 1) {
65
this->numerator = m;
66
this->denominator = n;
67
this->reduction();
68
}
69
70
void reduction() {
71
long x = gcd(this->numerator, this->denominator);
72
if ((llabs(this->denominator) > global_setting.max_range) || (llabs(this->numerator) > global_setting.max_range)) {
73
this->numerator = 1;
74
this->denominator = 0;
75
x = 1;
76
}
77
this->numerator /= x;
78
this->denominator /= x;
79
if (this->denominator < 0) {
80
this->numerator *= -1;
81
this->denominator *= -1;
82
}
83
if (!this->numerator) {
84
this->denominator = 1;
85
}
86
if (!this->denominator) {
87
this->numerator = 1;
88
}
89
if ((abs(this->denominator)>global_setting.max_range) || (abs(this->numerator) > global_setting.max_range)) {
90
this->numerator = 1;
91
this->denominator = 0;
92
}
93
return;
94
}
95
96
fraction operator + (const fraction x) const {
97
if (is_bad_value(*this))return *this;
98
if (is_bad_value(x))return x;
99
fraction result;
100
result.numerator = this->numerator * x.denominator + this->denominator * x.numerator;
101
result.denominator = this->denominator * x.denominator;
102
result.reduction();
103
return result;
104
}
105
106
107
fraction operator - (const fraction& x) const {
108
if (is_bad_value(*this))return *this;
109
if (is_bad_value(x))return x;
110
fraction result;
111
result.numerator = this->numerator * x.denominator - this->denominator * x.numerator;
112
result.denominator = this->denominator * x.denominator;
113
result.reduction();
114
return result;
115
}
116
117
fraction operator * (const fraction& x) const {
118
if (is_bad_value(*this))return *this;
119
if (is_bad_value(x))return x;
120
fraction result;
121
result.numerator = this->numerator * x.numerator;
122
result.denominator = this->denominator * x.denominator;
123
result.reduction();
124
return result;
125
}
126
127
fraction operator / (const fraction& x) const {
128
if (is_bad_value(*this))return *this;
129
if (is_bad_value(x))return x;
130
fraction stan_bad_value(1, 0);
131
if (x.numerator == 0) {
132
return stan_bad_value;
133
}
134
fraction result;
135
result.numerator = this->numerator * x.denominator;
136
result.denominator = this->denominator * x.numerator;
137
result.reduction();
138
if (is_bad_value(result)) {
139
result.numerator = 1;
140
result.denominator = 0;
141
}
142
return result;
143
}
144
145
fraction operator ^ (fraction x) const {
146
if (is_bad_value(*this))return *this;
147
if (is_bad_value(x))return x;
148
149
x.reduction();
150
if (x.denominator != 1) {
151
fraction bad_value;
152
bad_value.numerator = 1;
153
bad_value.denominator = 0;
154
return bad_value;
155
}
156
long index = x.numerator;
157
158
fraction result;
159
result.numerator = (long)powl(this->numerator, abs(index));
160
result.denominator = (long)powl(this->denominator, abs(index));
161
if (index < 0) {
162
long temp;
163
temp = result.numerator;
164
result.numerator = result.denominator;
165
result.denominator = temp;
166
}
167
result.reduction();
168
return result;
169
}
170
};
171
172
ostream& operator << (ostream& out, const fraction& frac) {
173
174
out << '(' << frac.numerator << ".0/" << frac.denominator << ".0)";
175
return out;
176
177
long long n = frac.numerator;
178
long long d = frac.denominator;
179
long long integer = n / d;
180
long long f = n % d;
181
182
if (f == 0) {
183
out << integer;
184
}
185
else {
186
if (integer) {
187
out << integer << '\'' << f << '/' << d;
188
}
189
else {
190
out << f << '/' << d;
191
}
192
}
193
return out;
194
195
196
}
197
198
/*
199
* unit test for fraction
200
*/
201
void fraction_test() {
202
fraction a(1, 0), b(0, 1), c(2, 3), d(5, 6), e(8, 4), g(18, 9);
203
fraction x;
204
x = a + b;
205
x = a * b;
206
x = a - b;
207
x = a / b;
208
x = a / c;
209
x = a + c;
210
x = a * c;
211
x = a - c;
212
x = b + c;
213
x = b - c;
214
x = b * c;
215
x = b / c;
216
x = c * d;
217
x = c / d;
218
x = c + d;
219
x = c - d;
220
x = a ^ b;
221
x = a ^ c;
222
x = a ^ d;
223
x = a ^ e;
224
x = a ^ g;
225
x = b ^ c;
226
x = b ^ a;
227
x = b ^ d;
228
x = b ^ e;
229
x = b ^ g;
230
x = e * g;
231
x = e - g;
232
x = e + g;
233
x = e / g;
234
}
235
236
/*
237
* is_bad_value
238
*/
239
bool is_bad_value(const fraction& x) {
240
if (!x.denominator) {
241
return true;
242
}
243
else {
244
return false;
245
}
246
}
247
248
bool is_bad_value(const double& x) {
249
// todo: error
250
if (isnan(x) || isinf(x)) {
251
return true;
252
}
253
else {
254
return false;
255
}
256
}
257
258
/*
259
* AST
260
*/
261
enum ASTNodeType { TYPE_ADD = 0, TYPE_MINUS = 1, TYPE_MUL = 2, TYPE_DIV = 3, TYPE_POWER = 4, TYPE_FRACTION = 5, TYPE_DOUBLE = 6 };
262
263
struct ASTNode;
264
265
union NodeData {
266
fraction frac;
267
double real;
268
pair<ASTNode*, ASTNode*> node;
269
270
NodeData() {
271
real = 0;
272
}
273
};
274
275
struct ASTNode {
276
ASTNodeType type;
277
NodeData data;
278
279
ASTNode() {
280
type = TYPE_DOUBLE;
281
}
282
283
~ASTNode() {
284
if (type != TYPE_FRACTION && type != TYPE_DOUBLE) {
285
delete data.node.first;
286
delete data.node.second;
287
}
288
}
289
};
290
291
292
293
/*
294
* random ast
295
*/
296
enum cal_mode { MODE_FRACTION, MODE_REAL };
297
298
inline ASTNode* random_value(cal_mode mode) {
299
ASTNode* node = new ASTNode();
300
int m, n;
301
switch (mode) {
302
case MODE_FRACTION:
303
node->type = TYPE_FRACTION;
304
m = rand() % (global_setting.max_num - 1) + 1;
305
n = rand() % (m * 5);
306
if (global_setting.has_fraction) {
307
node->data.frac = fraction(n, m);
308
}
309
else {
310
node->data.frac = fraction(m);
311
}
312
break;
313
314
case MODE_REAL:
315
node->type = TYPE_DOUBLE;
316
double base = pow(10, global_setting.precision);
317
node->data.real = floor((rand() / (double)RAND_MAX)*global_setting.max_num*base) / base;
318
break;
319
}
320
return node;
321
}
322
323
ASTNode* random_ast(cal_mode mode) {
324
int n = global_setting.max_opearators <= 1 ? 1 : rand() % (global_setting.max_opearators - 1) + 1;
325
ASTNode* num1 = random_value(mode);
326
for (int i = 0; i<n; i++) {
327
ASTNode* num2 = random_value(mode);
328
if (rand() % 2) swap(num1, num2);
329
int r = rand() % 17;
330
ASTNode* new_node = new ASTNode();
331
332
333
if (r-- == 16 && (num2->type == TYPE_FRACTION || num2->type == TYPE_FRACTION) && (num1->type != TYPE_POWER) && global_setting.has_power) {
334
if (mode == MODE_FRACTION) num2->data.frac = fraction(rand() % 4 + 1);
335
else num2->data.real = rand() % 2 + 2;
336
337
new_node->type = TYPE_POWER;
338
new_node->data.node.first = num1;
339
new_node->data.node.second = num2;
340
}
341
else {
342
if (global_setting.has_mul_div) {
343
new_node->type = (ASTNodeType)(r / 4);
344
if (mode == MODE_FRACTION && !global_setting.has_fraction) {
345
r = rand() % 10;
346
if (r-- == 9) new_node->type = TYPE_DIV;
347
else new_node->type = (ASTNodeType)(r / 3);
348
}
349
}
350
else {
351
new_node->type = (ASTNodeType)(r / 8);
352
}
353
new_node->data.node.first = num1;
354
new_node->data.node.second = num2;
355
}
356
357
num1 = new_node;
358
}
359
return num1;
360
}
361
362
363
long long hash_value;
364
ASTNode* calc_asttree(ASTNode* root) {
365
ASTNode* result = new ASTNode();
366
result->type = TYPE_FRACTION;
367
result->data.frac;
368
ASTNode* temp_a = new ASTNode();
369
ASTNode* temp_b = new ASTNode();
370
switch (root->type) {
371
case TYPE_FRACTION:
372
result->type = TYPE_FRACTION;
373
result->data.frac = root->data.frac;
374
break;
375
case TYPE_DOUBLE:
376
result->type = TYPE_DOUBLE;
377
result->data.real = root->data.real;
378
break;
379
case TYPE_ADD:
380
temp_a = calc_asttree(root->data.node.first);
381
temp_b = calc_asttree(root->data.node.second);
382
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
383
result->type = TYPE_FRACTION;
384
result->data.frac = temp_a->data.frac + temp_b->data.frac;
385
}
386
else {
387
result->type = TYPE_DOUBLE;
388
double a, b;
389
if (temp_a->type == TYPE_FRACTION) {
390
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
391
}
392
else if (temp_a->type == TYPE_DOUBLE) {
393
a = temp_a->data.real;
394
}
395
if (temp_b->type == TYPE_FRACTION) {
396
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
397
}
398
else if (temp_b->type == TYPE_DOUBLE) {
399
b = temp_b->data.real;
400
}
401
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a + b);
402
}
403
break;
404
case TYPE_MINUS:
405
temp_a = calc_asttree(root->data.node.first);
406
temp_b = calc_asttree(root->data.node.second);
407
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
408
result->type = TYPE_FRACTION;
409
result->data.frac = temp_a->data.frac - temp_b->data.frac;
410
411
}
412
else {
413
result->type = TYPE_DOUBLE;
414
double a, b;
415
if (temp_a->type == TYPE_FRACTION) {
416
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
417
}
418
else if (temp_a->type == TYPE_DOUBLE) {
419
a = temp_a->data.real;
420
}
421
if (temp_b->type == TYPE_FRACTION) {
422
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
423
}
424
else if (temp_b->type == TYPE_DOUBLE) {
425
b = temp_b->data.real;
426
}
427
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a - b);
428
}
429
break;
430
case TYPE_MUL:
431
temp_a = calc_asttree(root->data.node.first);
432
temp_b = calc_asttree(root->data.node.second);
433
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
434
result->type = TYPE_FRACTION;
435
result->data.frac = temp_a->data.frac * temp_b->data.frac;
436
437
}
438
else {
439
result->type = TYPE_DOUBLE;
440
double a, b;
441
if (temp_a->type == TYPE_FRACTION) {
442
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
443
}
444
else if (temp_a->type == TYPE_DOUBLE) {
445
a = temp_a->data.real;
446
}
447
if (temp_b->type == TYPE_FRACTION) {
448
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
449
}
450
else if (temp_b->type == TYPE_DOUBLE) {
451
b = temp_b->data.real;
452
}
453
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : (a*b);
454
}
455
break;
456
case TYPE_DIV:
457
temp_a = calc_asttree(root->data.node.first);
458
temp_b = calc_asttree(root->data.node.second);
459
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
460
result->type = TYPE_FRACTION;
461
result->data.frac = temp_a->data.frac / temp_b->data.frac;
462
463
}
464
else {
465
result->type = TYPE_DOUBLE;
466
double a, b;
467
if (temp_a->type == TYPE_FRACTION) {
468
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
469
}
470
else if (temp_a->type == TYPE_DOUBLE) {
471
a = temp_a->data.real;
472
}
473
if (temp_b->type == TYPE_FRACTION) {
474
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
475
}
476
else if (temp_b->type == TYPE_DOUBLE) {
477
b = temp_b->data.real;
478
}
479
result->data.real = is_bad_value(a) || is_bad_value(b) || fabs(b) <= 1e-3 ? INFINITY : (a / b);
480
}
481
break;
482
case TYPE_POWER:
483
temp_a = calc_asttree(root->data.node.first);
484
temp_b = calc_asttree(root->data.node.second);
485
if (temp_a->type == TYPE_FRACTION && temp_b->type == TYPE_FRACTION) {
486
result->type = TYPE_FRACTION;
487
result->data.frac = temp_a->data.frac ^ temp_b->data.frac;
488
489
}
490
else {
491
result->type = TYPE_DOUBLE;
492
double a, b;
493
if (temp_a->type == TYPE_FRACTION) {
494
a = (double)temp_a->data.frac.numerator / (double)temp_a->data.frac.denominator;
495
}
496
else if (temp_a->type == TYPE_DOUBLE) {
497
a = temp_a->data.real;
498
}
499
if (temp_b->type == TYPE_FRACTION) {
500
b = (double)temp_b->data.frac.numerator / (double)temp_b->data.frac.denominator;
501
}
502
else if (temp_b->type == TYPE_DOUBLE) {
503
b = temp_b->data.real;
504
}
505
result->data.real = is_bad_value(a) || is_bad_value(b) ? INFINITY : powl(a, b);
506
}
507
break;
508
}
509
long long value = (long long)(root->type == TYPE_FRACTION ? (root->data.frac.numerator / (double)root->data.frac.denominator) : root->data.real);
510
hash_value = (hash_value * 19260817 + value) % (long long)(1e9 + 7);
511
delete temp_a;
512
delete temp_b;
513
if (result->type == TYPE_FRACTION) {
514
if ( (result->data.frac.denominator > global_setting.max_range*100 || result->data.frac.numerator < 0) ||
515
(result->data.frac.denominator != 1 && !global_setting.has_fraction)) {
516
result->data.frac.numerator = 1;
517
result->data.frac.denominator = 0;
518
}
519
} else if (result->type == TYPE_DOUBLE && (result->data.real <0|| result->data.real>global_setting.max_range*10)) {
520
result->data.real = INFINITY;
521
}
522
return result;
523
}
524
525
ASTNode* ast_eval(ASTNode* root) {
526
hash_value = 0;
527
return calc_asttree(root);
528
}
529
530
/*
531
* Expr := AddExpr | Expr + AddExpr
532
* AddExpr := MulExpr | AddExpr * MulExpr
533
* MulExpr := PowExpr | MulExpr ^ PowExpr
534
* PowExpr := Number | (Expr)
535
*/
536
enum ExprType { EXPR_EXPR, EXPR_ADDEXPR, EXPR_MULEXPR, EXPR_POWEXPR };
537
538
void ast_output_expr(ASTNode* root, stringstream& ss);
539
void ast_output_addexpr(ASTNode* root, stringstream& ss);
540
void ast_output_mulexpr(ASTNode* root, stringstream& ss);
541
void ast_output_powexpr(ASTNode* root, stringstream& ss);
542
void ast_output_number(ASTNode* root, stringstream& ss);
543
544
void ast_output_expr(ASTNode* root, stringstream& ss) {
545
switch (root->type) {
546
case TYPE_ADD:case TYPE_MINUS:
547
ast_output_expr(root->data.node.first, ss);
548
ss << (root->type == TYPE_ADD ? " + " : " - ");
549
ast_output_addexpr(root->data.node.second, ss);
550
break;
551
552
default:
553
ast_output_addexpr(root, ss);
554
break;
555
}
556
}
557
558
void ast_output_addexpr(ASTNode* root, stringstream& ss) {
559
switch (root->type) {
560
case TYPE_MUL:case TYPE_DIV:
561
ast_output_addexpr(root->data.node.first, ss);
562
ss << (root->type == TYPE_MUL ? " * " : " / ");
563
ast_output_mulexpr(root->data.node.second, ss);
564
break;
565
566
default:
567
ast_output_mulexpr(root, ss);
568
break;
569
}
570
}
571
572
void ast_output_mulexpr(ASTNode* root, stringstream& ss) {
573
switch (root->type) {
574
case TYPE_POWER:
575
ast_output_mulexpr(root->data.node.first, ss);
576
577
ss << " ** ";
578
579
ss << " ** ";
580
581
ss << "^";
582
583
ast_output_powexpr(root->data.node.second, ss);
584
break;
585
default:
586
ast_output_powexpr(root, ss);
587
break;
588
}
589
}
590
591
void ast_output_powexpr(ASTNode* root, stringstream& ss) {
592
switch (root->type) {
593
case TYPE_FRACTION:
594
ss << root->data.frac;
595
break;
596
case TYPE_DOUBLE:
597
ss << root->data.real;
598
break;
599
default:
600
ss << '(';
601
ast_output_expr(root, ss);
602
ss << ')';
603
break;
604
}
605
}
606
607
set<long long> ans_set;
608
609
CORE15_API void set_setting(
610
int max_opearators,
611
long max_range,
612
int precision,
613
int has_fraction,
614
int has_real,
615
int has_mul_div,
616
int has_power) {
617
618
if (max_opearators != -1) global_setting.max_opearators = max_opearators;
619
if (max_range != -1) global_setting.max_range = max_range;
620
if (precision != -1) global_setting.precision = precision;
621
if (has_fraction != -1) global_setting.has_fraction = has_fraction != 0;
622
if (has_real != -1) global_setting.has_real = has_real != 0;
623
if (has_mul_div != -1) global_setting.has_mul_div = has_mul_div != 0;
624
if (has_power != -1) global_setting.has_power = has_power != 0;
625
global_setting.max_num = max_range>=20 ? max_range / 10 : max_range;
626
}
627
628
629
int c1=0,c2=0;
630
631
CORE15_API void generate(string* question, string* answer) {
632
cal_mode mode;
633
int magic = global_setting.has_fraction ? 32 : 3;
634
if (global_setting.has_real && rand() % magic == 0) {
635
mode = MODE_REAL;
636
} else{
637
mode = MODE_FRACTION;
638
}
639
question->clear();
640
answer->clear();
641
642
ASTNode* node = random_ast(mode);
643
ASTNode* ret = ast_eval(node);
644
bool bad_value = false;
645
646
stringstream s1, s2;
647
s1.setf(std::ios::fixed, std::ios::floatfield);
648
s2.setf(std::ios::fixed, std::ios::floatfield);
649
650
s2.precision(global_setting.precision);
651
if (ret->type == TYPE_DOUBLE && !is_bad_value(ret->data.real)) {
652
s2 << ret->data.real;
653
}
654
else if (ret->type == TYPE_FRACTION && !is_bad_value(ret->data.frac)) {
655
656
s2 << (ret->data.frac.numerator / (double)ret->data.frac.denominator);
657
658
s2 << ret->data.frac;
659
660
}
661
else {
662
bad_value = true;
663
}
664
*answer = s2.str();
665
666
if (bad_value || ans_set.find(hash_value) != ans_set.end()) {
667
generate(question, answer);
668
delete node;
669
delete ret;
670
return;
671
}
672
else {
673
ans_set.insert(hash_value);
674
}
675
676
s1.precision(global_setting.precision);
677
ast_output_expr(node, s1);
678
*question = s1.str();
679
680
delete node;
681
delete ret;
682
683
684
if(mode==MODE_FRACTION) c1++;
685
else c2++;
686
687
688
return;
689
}
690
691
692
693
// for unit test
694
int main() {
695
// todo: random
696
FILE* file = NULL;
697
const long long test_num = 100000;
698
const long long test_groups = 100;
699
for (long long i = 0; i<test_num; i++) {
700
if (i % (test_num / test_groups) == 0) {
701
stringstream ss;
702
ss << "test" << i / (test_num / test_groups) << ".py";
703
if (file) fclose(file);
704
file = fopen(ss.str().c_str(), "w");
705
}
706
string que, ans;
707
generate(&que, &ans);
708
fprintf(file, "assert(%lld>=0 and abs((%s)-(%s))<5e-2)\n", i, que.c_str(), ans.c_str());
709
}
710
fclose(file);
711
return 0;
712
}
713
714
int main() {
715
srand(time(NULL));
716
for (long long i = 0; i<200; i++) {
717
string que, ans;
718
generate(&que, &ans);
719
cout << que << " = " << ans << endl;
720
}
721
cout<<c1<<" "<<c2<<endl;
722
return 0;
723
}
724