<!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