使用java或C/C++编程语言,独立完成一个3到5个运算符的四则运算练习的软件
1 程序可接收一个输入参数n,然后随机产生n道加减乘除(分别使用符号±*÷来表示)练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。
2 每个练习题至少要包含2种运算符。同时,由于小学生没有分数与负数的概念,你所出的练习题在运算过程中不得出现负数与非整数,比如不能出 3÷5+2=2.6,2-5+10=7等算式。
3 练习题生成好后,将你的学号与生成的n道练习题及其对应的正确答案输出到文件“result.txt”中,不要输出额外信息,文件目录与程序目录一致。
4 当程序接收的参数为4时,以下为一个输出文件示例。
2018010203
13+17-1=29
11*15-5=160
3+10+4-16=1
15÷5+3-2=4
1 支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号数必须大于2对,且不得超过运算符的个数。
2 扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。
个人博客
https://linxi99.gitee.io/
https://linxi99.gitee.io/20190319/Arithmetic-Device/
https://blog.csdn.net/linxilinxilinxi
https://blog.csdn.net/linxilinxilinxi/article/details/88548481
项目地址
https://gitee.com/linxi99/four_operational_generators
#include
using namespace std;
mt19937 mt(time(0));
map<int, char> op;
map<int, int> pri;
struct node{
int id, val;
node(int id = -1, int val = -1):id(id), val(val){}
};
int tot, operatorNum, operandNum, bracketNum, ans;
int hasBracket[15], operators[15], operands[15];
int RPN[55], fac[105];
stack<int> opr;
stack<node> opd;
void init1(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
operands[i] = -1;
hasBracket[i] = -1;
}
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
void init2(){
tot = 0;
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
bool getOperands(){
init2();
if(hasBracket[0] != -1) opr.push(hasBracket[0]);
RPN[tot++] = 0;
// puts("**3.1**");
for(int i = 1; i < operandNum; ++i){
// printf("%d\n", i);
while(true){
if(opr.empty() || opr.top() == 14 || pri[operators[i - 1]] > pri[opr.top()]){
opr.push(operators[i - 1]);
break;
}
RPN[tot++] = opr.top();
opr.pop();
}
if(hasBracket[i] == 14){
opr.push(hasBracket[i]);
}
RPN[tot++] = i;
if(hasBracket[i] == 15){
while(opr.top() != 14){
RPN[tot++] = opr.top();
opr.pop();
}
opr.pop();
}
}
// puts("**3.2**");
while(!opr.empty()){
RPN[tot++] = opr.top();
opr.pop();
}
// for(int i = 0; i < tot; ++i){
// printf("%d ", RPN[i]);
// }
// puts("");
// puts("**3.3**");
for(int i = 0; i < tot; ++i){
if(RPN[i] < 10){
int x = mt()%66 + 1;
operands[RPN[i]] = x;
opd.push(node(RPN[i], x));
continue;
}
if(RPN[i] == 13){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(a.val%b.val == 0){
opd.push(node(-1, a.val/b.val));
continue;
}
if(b.id == -1) return false;
int cnt = 0;
for(int j = 1; j <= a.val; ++j){
if(a.val%j) continue;
fac[cnt++] = j;
}
int x = mt()%cnt;
operands[b.id] = fac[x];
opd.push(node(-1, a.val/fac[x]));
}else if(RPN[i] == 11){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
int dt = a.val - b.val;
if(dt <= 0) return false;
opd.push(node(-1, dt));
}else{
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(RPN[i] == 10) opd.push(node(-1, a.val + b.val));
if(RPN[i] == 12) opd.push(node(-1, a.val*b.val));
}
}
// puts("**3.4**");
ans = opd.top().val; opd.pop();
if(ans < 0 || ans > 1000) return false;
// for(int i = 0; i < tot; ++i){
// printf("%d ", RPN[i]);
// }
// puts("");
return true;
}
bool solve(){
init1();
operatorNum = mt()%3 + 3;
operandNum = operatorNum + 1;
bracketNum = min((int)(operandNum/2), (int)(mt()%2 + 2));
// printf("%d %d %d\n", operatorNum, operandNum, bracketNum);
for(int i = 0; i < operatorNum; ++i) operators[i] = mt()%4 + 10;
for(int i = 0; i < bracketNum*2; ++i){
int x = mt()%operandNum;
while(hasBracket[x] != -1) x = mt()%operandNum;
hasBracket[x] = 0;
}
// puts("**1**");
bool lf = true;
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13){
if(hasBracket[i] == 14){
for(int j = 0; j < 15; ++j) hasBracket[j] = -1;
break;
}
if(i < operandNum - 1 && operators[i] >= 12) return false;
}
}
// puts("**2**");
for(int i = 0; i < operandNum; ++i){
if(hasBracket[i] == -1) continue;
if(lf) hasBracket[i] = 14;
else hasBracket[i] = 15;
lf = (!lf);
}
// puts("**3**");
if(!getOperands()) return false;
// puts("**4**");
if(hasBracket[0] != -1) printf("(");
printf("%d", operands[0]);
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13) printf("");
else printf("%c", op[operators[i - 1]]);
if(hasBracket[i] == 14) printf("(");
printf("%d", operands[i]);
if(hasBracket[i] == 15) printf(")");
}
printf("=%d\n", ans);
return true;
}
int main(){
op[10] = '+', op[11] = '-';
op[12] = '*';
op[14] = '(', op[15] = ')';
pri[10] = 0, pri[11] = 0;
pri[12] = 1, pri[13] = 1;
pri[14] = 2, pri[15] = 2;
freopen("../result.txt","w",stdout);
int n;
scanf("%d", &n);
puts("2017012449");
while(n--) while(!solve());
return 0;
}
#include
using namespace std;
mt19937 mt(time(0)); // 随机数生成器
map<int, char> op; // 运算符id及其符号映射
map<int, int> pri; // 运算符id及其优先级映射
// 10表示加号 运算优先级为 0
// 11表示减号 运算优先级为 0
// 12表示乘号 运算优先级为 1
// 13表示除号 运算优先级为 1
// 14表示左括号 运算优先级为 2
// 15表示右括号 运算优先级为 2
// 运算数结构体,id为运算数的位置,val为运算数的值
struct node{
int id, val;
node(int id = -1, int val = -1):id(id), val(val){}
};
// tot:逆波兰表达式的长度,operatorNum:运算符个数
// operandNum:运算数的个数,bracketNum:括号对数,ans:运算式最终答案
// hasBracket:某个数字处是否有括号,-1表示无括号,14表示左括号,15表示右括号
// operators:对应位置的运算符种类 operands:表示对应位置运算数的值
// RPN:存储逆波兰表达式 fac:存储某个数的约数
// opr:中缀表达式转后缀表达式时的运算符栈
// opd:中缀表达式转后缀表达式时的运算数栈
int tot, operatorNum, operandNum, bracketNum, ans;
int hasBracket[15], operators[15], operands[15];
int RPN[55], fac[105];
stack<int> opr;
stack<node> opd;
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
void init0(){
op[10] = '+', op[11] = '-';
op[12] = '*';
op[14] = '(', op[15] = ')';
pri[10] = 0, pri[11] = 0;
pri[12] = 1, pri[13] = 1;
pri[14] = 2, pri[15] = 2;
freopen("../result.txt", "w", stdout);
}
// 初始化函数一:在每次调用solve()时进行初始化
void init1(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
operands[i] = -1;
hasBracket[i] = -1;
}
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数二:在每次调用getOperands()时进行初始化
void init2(){
tot = 0;
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 获取运算数的值的函数,如果返回 true 则表示获取成功,否则表示获取失败
bool getOperands(){
init2();
// 将中缀表达式转换成后缀表达式(也就是逆波兰表达式)
if(hasBracket[0] != -1) opr.push(hasBracket[0]);
RPN[tot++] = 0;
for(int i = 1; i < operandNum; ++i){
while(true){
if(opr.empty() || opr.top() == 14 || pri[operators[i - 1]] > pri[opr.top()]){
opr.push(operators[i - 1]);
break;
}
RPN[tot++] = opr.top();
opr.pop();
}
if(hasBracket[i] == 14){
opr.push(hasBracket[i]);
}
RPN[tot++] = i;
if(hasBracket[i] == 15){
while(opr.top() != 14){
RPN[tot++] = opr.top();
opr.pop();
}
opr.pop();
}
}
while(!opr.empty()){
RPN[tot++] = opr.top();
opr.pop();
}
// 转换成逆波兰表达式后便可以进行尝试填数
for(int i = 0; i < tot; ++i){
// 如果为运算数则随机为其赋值
if(RPN[i] < 10){
int x = mt()%66 + 1;
operands[RPN[i]] = x;
opd.push(node(RPN[i], x));
continue;
}
//如果为除法,要将除数随机分配为被除数的一个因子
//如果为减法,要注意减数不能大于被减数
if(RPN[i] == 13){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(a.val%b.val == 0){
opd.push(node(-1, a.val/b.val));
continue;
}
if(b.id == -1) return false;
int cnt = 0;
for(int j = 1; j <= a.val; ++j){
if(a.val%j) continue;
fac[cnt++] = j;
}
int x = mt()%cnt;
operands[b.id] = fac[x];
opd.push(node(-1, a.val/fac[x]));
}else if(RPN[i] == 11){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
int dt = a.val - b.val;
if(dt <= 0) return false;
opd.push(node(-1, dt));
}else{
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(RPN[i] == 10) opd.push(node(-1, a.val + b.val));
if(RPN[i] == 12) opd.push(node(-1, a.val*b.val));
}
}
ans = opd.top().val; opd.pop();
//控制最终运算结果的范围,可根据需要进行调节
if(ans < 0 || ans > 1000) return false;
return true;
}
bool solve(){
init1();
// 随机生成运算符的个数 3~5 ,及运算数个数 4~6
operatorNum = mt()%3 + 3;
operandNum = operatorNum + 1;
// 随机生成括号个数
bracketNum = min((int)(operandNum/2), (int)(mt()%3 + 2));
// 随机生成运算符的种类
for(int i = 0; i < operatorNum; ++i) operators[i] = mt()%4 + 10;
// 随机生成括号位置
for(int i = 0; i < bracketNum*2; ++i){
int x = mt()%operandNum;
while(hasBracket[x] != -1) x = mt()%operandNum;
hasBracket[x] = 0;
}
// 根据相对位置确定括号为左括号还是右括号
bool lf = true;
for(int i = 0; i < operandNum; ++i){
if(hasBracket[i] == -1) continue;
if(lf) hasBracket[i] = 14;
else hasBracket[i] = 15;
lf = (!lf);
}
// 到这里已经将等式预处理成了(a+b)/c*(d-e)的类似形式
// 预处理结束之后,我们就要尝试将 a b c d e 确定为具体的数
if(!getOperands()) return false;
// 在获取完运算数之后,便可以输出我们得到的等式了
if(hasBracket[0] != -1) printf("(");
printf("%d", operands[0]);
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13) printf("÷");
else printf("%c", op[operators[i - 1]]);
if(hasBracket[i] == 14) printf("(");
printf("%d", operands[i]);
if(hasBracket[i] == 15) printf(")");
}
printf("=%d\n", ans);
return true;
}
int main(){
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
init0();
//读入需要生成的运算式数量
int n;
scanf("%d", &n);
//输出学号及运算式
puts("2017012449");
while(n--) while(!solve()) ;
return 0;
}
#include
using namespace std;
mt19937 mt(time(0)); // 随机数生成器
map<int, char> op; // 运算符id及其符号映射
map<int, int> pri; // 运算符id及其优先级映射
// 10表示加号 运算优先级为 0
// 11表示减号 运算优先级为 0
// 12表示乘号 运算优先级为 1
// 13表示除号 运算优先级为 1
// 14表示左括号 运算优先级为 2
// 15表示右括号 运算优先级为 2
// 运算数结构体,id为运算数的位置,val为运算数的值
struct node{
int id, val;
node(int id = -1, int val = -1):id(id), val(val){}
};
// tot:逆波兰表达式的长度,operatorNum:运算符个数
// operandNum:运算数的个数,bracketNum:括号对数,ans:运算式最终答案
// hasBracket:某个数字处是否有括号,-1表示无括号,14表示左括号,15表示右括号
// operators:对应位置的运算符种类 operands:表示对应位置运算数的值
// RPN:存储逆波兰表达式 fac:存储某个数的约数
// opr:中缀表达式转后缀表达式时的运算符栈
// opd:中缀表达式转后缀表达式时的运算数栈
int tot, operatorNum, operandNum, bracketNum, ans;
int hasBracket[15], operators[15], operands[15];
int RPN[55], fac[105];
stack<int> opr;
stack<node> opd;
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
void init0(){
op[10] = '+', op[11] = '-';
op[12] = '*';
op[14] = '(', op[15] = ')';
pri[10] = 0, pri[11] = 0;
pri[12] = 1, pri[13] = 1;
pri[14] = 2, pri[15] = 2;
freopen("../result.txt", "w", stdout);
}
// 初始化函数一:在每次调用solve()时进行初始化
void init1(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
operands[i] = -1;
hasBracket[i] = -1;
}
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数二:在每次调用getOperands()时进行初始化
void init2(){
tot = 0;
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 获取运算数的值的函数,如果返回 true 则表示获取成功,否则表示获取失败
bool getOperands(){
init2();
// 将中缀表达式转换成后缀表达式(也就是逆波兰表达式)
if(hasBracket[0] != -1) opr.push(hasBracket[0]);
RPN[tot++] = 0;
for(int i = 1; i < operandNum; ++i){
while(true){
if(opr.empty() || opr.top() == 14 || pri[operators[i - 1]] > pri[opr.top()]){
opr.push(operators[i - 1]);
break;
}
RPN[tot++] = opr.top();
opr.pop();
}
if(hasBracket[i] == 14){
opr.push(hasBracket[i]);
}
RPN[tot++] = i;
if(hasBracket[i] == 15){
while(opr.top() != 14){
RPN[tot++] = opr.top();
opr.pop();
}
opr.pop();
}
}
while(!opr.empty()){
RPN[tot++] = opr.top();
opr.pop();
}
// 转换成逆波兰表达式后便可以进行尝试填数
for(int i = 0; i < tot; ++i){
// 如果为运算数则随机为其赋值
if(RPN[i] < 10){
int x = mt()%66 + 1;
operands[RPN[i]] = x;
opd.push(node(RPN[i], x));
continue;
}
//如果为除法,要将除数随机分配为被除数的一个因子
//如果为减法,要注意减数不能大于被减数
if(RPN[i] == 13){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(a.val%b.val == 0){
opd.push(node(-1, a.val/b.val));
continue;
}
if(b.id == -1) return false;
int cnt = 0;
for(int j = 1; j <= a.val; ++j){
if(j >= 100) break;
if(a.val%j) continue;
fac[cnt++] = j;
}
int x = mt()%cnt;
operands[b.id] = fac[x];
opd.push(node(-1, a.val/fac[x]));
}else if(RPN[i] == 11){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
int dt = a.val - b.val;
if(dt <= 0) return false;
opd.push(node(-1, dt));
}else{
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(RPN[i] == 10) opd.push(node(-1, a.val + b.val));
if(RPN[i] == 12) opd.push(node(-1, a.val*b.val));
}
}
ans = opd.top().val; opd.pop();
//控制最终运算结果的范围,可根据需要进行调节
if(ans < 0 || ans > 1000) return false;
return true;
}
bool solve(){
init1();
// 随机生成运算符的个数 3~5 ,及运算数个数 4~6
operatorNum = mt()%3 + 3;
operandNum = operatorNum + 1;
// 随机生成括号个数
bracketNum = min((int)(operandNum/2), (int)(mt()%3 + 2));
// 随机生成运算符的种类
for(int i = 0; i < operatorNum; ++i) operators[i] = mt()%4 + 10;
// 随机生成括号位置
for(int i = 0; i < bracketNum*2; ++i){
int x = mt()%operandNum;
while(hasBracket[x] != -1) x = mt()%operandNum;
hasBracket[x] = 0;
}
// 根据相对位置确定括号为左括号还是右括号
bool lf = true;
for(int i = 0; i < operandNum; ++i){
if(hasBracket[i] == -1) continue;
if(lf) hasBracket[i] = 14;
else hasBracket[i] = 15;
lf = (!lf);
}
// 到这里已经将等式预处理成了(a+b)/c*(d-e)的类似形式
// 预处理结束之后,我们就要尝试将 a b c d e 确定为具体的数
if(!getOperands()) return false;
// 在获取完运算数之后,便可以输出我们得到的等式了
if(hasBracket[0] != -1) printf("(");
printf("%d", operands[0]);
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13) printf("÷");
else printf("%c", op[operators[i - 1]]);
if(hasBracket[i] == 14) printf("(");
printf("%d", operands[i]);
if(hasBracket[i] == 15) printf(")");
}
printf("=%d\n", ans);
return true;
}
int main(){
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
init0();
//读入需要生成的运算式数量
int n;
scanf("%d", &n);
//输出学号及运算式
puts("2017012449");
while(n--) while(!solve()) ;
return 0;
}
#include
using namespace std;
mt19937 mt(time(0)); // 随机数生成器
map<int, char> op; // 运算符id及其符号映射
map<int, int> pri; // 运算符id及其优先级映射
// 10表示加号 运算优先级为 0
// 11表示减号 运算优先级为 0
// 12表示乘号 运算优先级为 1
// 13表示除号 运算优先级为 1
// 14表示左括号 运算优先级为 2
// 15表示右括号 运算优先级为 2
// 运算数结构体,id为运算数的位置,val为运算数的值
struct node{
int id, val;
node(int id = -1, int val = -1):id(id), val(val){}
};
// tot:逆波兰表达式的长度,operatorNum:运算符个数
// operandNum:运算数的个数,bracketNum:括号对数,ans:运算式最终答案
// hasBracket:某个数字处是否有括号,-1表示无括号,14表示左括号,15表示右括号
// operators:对应位置的运算符种类 operands:表示对应位置运算数的值
// RPN:存储逆波兰表达式 fac:存储某个数的约数
// opr:中缀表达式转后缀表达式时的运算符栈
// opd:中缀表达式转后缀表达式时的运算数栈
// numerators:对应位置运算数的分子 denominators:对应位置运算式的分母
int tot, operatorNum, operandNum, bracketNum, ans;
int hasBracket[15], operators[15], operands[15];
int RPN[55], fac[105], numerators[15], denominators[15];
stack<int> opr;
stack<node> opd;
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
void init0(){
op[10] = '+', op[11] = '-';
op[12] = '*';
op[14] = '(', op[15] = ')';
pri[10] = 0, pri[11] = 0;
pri[12] = 1, pri[13] = 1;
pri[14] = 2, pri[15] = 2;
freopen("../result.txt", "w", stdout);
}
// 初始化函数一:在每次调用solve()时进行初始化
void init1(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
operands[i] = -1;
hasBracket[i] = -1;
}
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数二:在每次调用getOperands()时进行初始化
void init2(){
tot = 0;
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数三:在每次调用solve2()时进行初始化
void init3(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
numerators[i] = -1;
denominators[i] = -1;
}
}
// 获取运算数的值的函数,如果返回 true 则表示获取成功,否则表示获取失败
bool getOperands(){
init2();
// 将中缀表达式转换成后缀表达式(也就是逆波兰表达式)
if(hasBracket[0] != -1) opr.push(hasBracket[0]);
RPN[tot++] = 0;
for(int i = 1; i < operandNum; ++i){
while(true){
if(opr.empty() || opr.top() == 14 || pri[operators[i - 1]] > pri[opr.top()]){
opr.push(operators[i - 1]);
break;
}
RPN[tot++] = opr.top();
opr.pop();
}
if(hasBracket[i] == 14){
opr.push(hasBracket[i]);
}
RPN[tot++] = i;
if(hasBracket[i] == 15){
while(opr.top() != 14){
RPN[tot++] = opr.top();
opr.pop();
}
opr.pop();
}
}
while(!opr.empty()){
RPN[tot++] = opr.top();
opr.pop();
}
// 转换成逆波兰表达式后便可以进行尝试填数
for(int i = 0; i < tot; ++i){
// 如果为运算数则随机为其赋值
if(RPN[i] < 10){
int x = mt()%66 + 1;
operands[RPN[i]] = x;
opd.push(node(RPN[i], x));
continue;
}
//如果为除法,要将除数随机分配为被除数的一个因子
//如果为减法,要注意减数不能大于被减数
if(RPN[i] == 13){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(a.val%b.val == 0){
opd.push(node(-1, a.val/b.val));
continue;
}
if(b.id == -1) return false;
int cnt = 0;
for(int j = 1; j <= a.val; ++j){
if(j >= 100) break;
if(a.val%j) continue;
fac[cnt++] = j;
}
int x = mt()%cnt;
operands[b.id] = fac[x];
opd.push(node(-1, a.val/fac[x]));
}else if(RPN[i] == 11){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
int dt = a.val - b.val;
if(dt <= 0) return false;
opd.push(node(-1, dt));
}else{
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(RPN[i] == 10) opd.push(node(-1, a.val + b.val));
if(RPN[i] == 12) opd.push(node(-1, a.val*b.val));
}
}
ans = opd.top().val; opd.pop();
//控制最终运算结果的范围,可根据需要进行调节
if(ans < 0 || ans > 1000) return false;
return true;
}
// solve1() 表示生成普通运算式
bool solve1(){
init1();
// 随机生成运算符的个数 3~5 ,及运算数个数 4~6
operatorNum = mt()%3 + 3;
operandNum = operatorNum + 1;
// 随机生成括号个数
bracketNum = min((int)(operandNum/2), (int)(mt()%3 + 2));
// 随机生成运算符的种类
for(int i = 0; i < operatorNum; ++i) operators[i] = mt()%4 + 10;
// 随机生成括号位置
for(int i = 0; i < bracketNum*2; ++i){
int x = mt()%operandNum;
while(hasBracket[x] != -1) x = mt()%operandNum;
hasBracket[x] = 0;
}
// 根据相对位置确定括号为左括号还是右括号
bool lf = true;
for(int i = 0; i < operandNum; ++i){
if(hasBracket[i] == -1) continue;
if(lf) hasBracket[i] = 14;
else hasBracket[i] = 15;
lf = (!lf);
}
// 到这里已经将等式预处理成了(a+b)/c*(d-e)的类似形式
// 预处理结束之后,我们就要尝试将 a b c d e 确定为具体的数
if(!getOperands()) return false;
// 在获取完运算数之后,便可以输出我们得到的等式了
if(hasBracket[0] != -1) printf("(");
printf("%d", operands[0]);
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13) printf("÷");
else printf("%c", op[operators[i - 1]]);
if(hasBracket[i] == 14) printf("(");
printf("%d", operands[i]);
if(hasBracket[i] == 15) printf(")");
}
printf("=%d\n", ans);
return true;
}
// solve2() 生成真分数运算式
// 由于只要求加减法,因此括号出现与否并不会影响答案
// 故solve2()中无须考虑括号与乘除号
bool solve2(){
init3();
operatorNum = mt()%3 + 3;
operandNum = operatorNum + 1;
for(int i = 0; i < operatorNum; ++i) operators[i] = mt()%2 + 10;
// 生成分子与分母,保证分子严格小于分母
denominators[0] = mt()%66 + 1;
numerators[0] = mt()%(max(denominators[0] - 22, 1)) + 1;
int g = __gcd(numerators[0], denominators[0]);
numerators[0] /= g, denominators[0] /= g;
int nowNume = numerators[0], nowDeno = denominators[0];
for(int i = 1; i < operandNum; ++i){
denominators[i] = mt()%66 + 1;
numerators[i] = mt()%(max(denominators[i] - 22, 1)) + 1;
g = __gcd(numerators[i], denominators[i]);
numerators[i] /= g, denominators[i] /= g;
g = __gcd(denominators[i], nowDeno);
int lcm = nowDeno*denominators[i]/g;
nowNume *= denominators[i]/g;
if(operators[i - 1] == 10){
nowNume += numerators[i]*nowDeno/g;
if(nowNume >= nowDeno) return false;
}else if(operators[i - 1] == 11){
nowNume -= numerators[i]*nowDeno/g;
if(nowNume <= 0) return false;
}
nowDeno = lcm;
g = __gcd(nowNume, nowDeno);
nowNume /= g, nowDeno /= g;
// 当运算过程中分子分母大于阈值(这里是 666 ),则重新生成算式
if(nowNume > 666 || nowDeno > 666) return false;
}
printf("%d/%d", numerators[0], denominators[0]);
for(int i = 1; i < operandNum; ++i){
printf("%c", op[operators[i - 1]]);
printf("%d/%d", numerators[i], denominators[i]);
}
printf("=%d/%d\n", nowNume, nowDeno);
return true;
}
// 这里 x 与模数控制分数运算式出现的概率
// 这里限定为 30% 的分数运算式, 70% 的普通运算式
void solve(){
int x = mt()%10;
if(x <= 2) while(!solve2()) ;
else while(!solve1()) ;
}
int main(){
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
init0();
//读入需要生成的运算式数量
int n;
scanf("%d", &n);
//输出学号及运算式
puts("2017012449");
while(n--) solve();
return 0;
}
#include
using namespace std;
mt19937 mt(time(0)); // 随机数生成器
map<int, char> op; // 运算符id及其符号映射
map<int, int> pri; // 运算符id及其优先级映射
// 10表示加号 运算优先级为 0
// 11表示减号 运算优先级为 0
// 12表示乘号 运算优先级为 1
// 13表示除号 运算优先级为 1
// 14表示左括号
// 15表示右括号
// 运算数结构体,id为运算数的位置,val为运算数的值
struct node{
int id, val;
node(int id = -1, int val = -1):id(id), val(val){}
};
// tot:逆波兰表达式的长度,operatorNum:运算符个数
// operandNum:运算数的个数,bracketNum:括号对数,ans:运算式最终答案
// hasBracket:某个数字处是否有括号,-1表示无括号,14表示左括号,15表示右括号
// operators:对应位置的运算符种类 operands:表示对应位置运算数的值
// RPN:存储逆波兰表达式 fac:存储某个数的约数
// opr:中缀表达式转后缀表达式时的运算符栈
// opd:中缀表达式转后缀表达式时的运算数栈
// numerators:对应位置运算数的分子 denominators:对应位置运算式的分母
int tot, operatorNum, operandNum, bracketNum, ans;
int hasBracket[15], operators[15], operands[15];
int RPN[55], fac[105], numerators[15], denominators[15];
stack<int> opr;
stack<node> opd;
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
void init0(){
op[10] = '+', op[11] = '-';
op[12] = '*';
op[14] = '(', op[15] = ')';
pri[10] = 0, pri[11] = 0;
pri[12] = 1, pri[13] = 1;
freopen("../result.txt", "w", stdout);
}
// 初始化函数一:在每次调用solve()时进行初始化
void init1(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
operands[i] = -1;
hasBracket[i] = -1;
}
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数二:在每次调用getOperands()时进行初始化
void init2(){
tot = 0;
while(!opr.empty()) opr.pop();
while(!opd.empty()) opd.pop();
}
// 初始化函数三:在每次调用solve2()时进行初始化
void init3(){
tot = 0;
for(int i = 0; i < 15; ++i){
operators[i] = -1;
numerators[i] = -1;
denominators[i] = -1;
}
}
// 生成一个范围在 [l, r] 中的随机数
int getNum(int l, int r){
int ret = mt()%(r - l + 1) + l;
return ret;
}
// 获取运算数的值的函数,如果返回 true 则表示获取成功,否则表示获取失败
bool getOperands(){
init2();
// 将中缀表达式转换成后缀表达式(也就是逆波兰表达式)
if(hasBracket[0] != -1) opr.push(hasBracket[0]);
RPN[tot++] = 0;
for(int i = 1; i < operandNum; ++i){
while(true){
if(opr.empty() || opr.top() == 14 || pri[operators[i - 1]] > pri[opr.top()]){
opr.push(operators[i - 1]);
break;
}
RPN[tot++] = opr.top();
opr.pop();
}
if(hasBracket[i] == 14){
opr.push(hasBracket[i]);
}
RPN[tot++] = i;
if(hasBracket[i] == 15){
while(opr.top() != 14){
RPN[tot++] = opr.top();
opr.pop();
}
opr.pop();
}
}
while(!opr.empty()){
RPN[tot++] = opr.top();
opr.pop();
}
// 转换成逆波兰表达式后便可以进行尝试填数
for(int i = 0; i < tot; ++i){
// 如果为运算数则随机为其赋值
if(RPN[i] < 10){
int x = getNum(1, 66);
operands[RPN[i]] = x;
opd.push(node(RPN[i], x));
continue;
}
//如果为除法,要将除数随机分配为被除数的一个因子
//如果为减法,要注意减数不能大于被减数
if(RPN[i] == 13){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(a.val%b.val == 0){
opd.push(node(-1, a.val/b.val));
continue;
}
if(b.id == -1) return false;
int cnt = 0;
for(int j = 1; j <= a.val; ++j){
if(j >= 100) break;
if(a.val%j) continue;
fac[cnt++] = j;
}
int x =getNum(0, cnt - 1);
operands[b.id] = fac[x];
opd.push(node(-1, a.val/fac[x]));
}else if(RPN[i] == 11){
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
int dt = a.val - b.val;
if(dt <= 0) return false;
opd.push(node(-1, dt));
}else{
node b = opd.top(); opd.pop();
node a = opd.top(); opd.pop();
if(RPN[i] == 10) opd.push(node(-1, a.val + b.val));
if(RPN[i] == 12) opd.push(node(-1, a.val*b.val));
}
}
ans = opd.top().val; opd.pop();
//控制最终运算结果的范围,可根据需要进行调节
if(ans < 0 || ans > 1000) return false;
return true;
}
// solve1() 表示生成普通运算式
bool solve1(bool flag){
init1();
// 随机生成运算符的个数 3~5 ,及运算数个数 4~6
operatorNum = getNum(3, 5);
operandNum = operatorNum + 1;
// 随机生成括号个数
bracketNum = min((int)(operandNum/2), getNum(2, 4));
if(flag) bracketNum = 0;
// 随机生成运算符的种类
for(int i = 0; i < operatorNum; ++i) operators[i] = getNum(10, 13);
// 随机生成括号位置
for(int i = 0; i < bracketNum*2; ++i){
int x = getNum(0, operandNum - 1);
while(hasBracket[x] != -1) x = getNum(0, operandNum - 1);
hasBracket[x] = 0;
}
// 根据相对位置确定括号为左括号还是右括号
bool lf = true;
for(int i = 0; i < operandNum; ++i){
if(hasBracket[i] == -1) continue;
if(lf) hasBracket[i] = 14;
else hasBracket[i] = 15;
lf = (!lf);
}
// 到这里已经将等式预处理成了(a+b)/c*(d-e)的类似形式
// 预处理结束之后,我们就要尝试将 a b c d e 确定为具体的数
if(!getOperands()) return false;
// 在获取完运算数之后,便可以输出我们得到的等式了
if(hasBracket[0] != -1) printf("(");
printf("%d", operands[0]);
for(int i = 1; i < operandNum; ++i){
if(operators[i - 1] == 13) printf("÷");
else printf("%c", op[operators[i - 1]]);
if(hasBracket[i] == 14) printf("(");
printf("%d", operands[i]);
if(hasBracket[i] == 15) printf(")");
}
printf("=%d\n", ans);
return true;
}
// solve2() 生成真分数运算式
// 由于只要求加减法,因此括号出现与否并不会影响答案
// 故solve2()中无须考虑括号与乘除号
bool solve2(){
init3();
operatorNum = getNum(3, 5);
operandNum = operatorNum + 1;
for(int i = 0; i < operatorNum; ++i) operators[i] = getNum(10, 11);
// 生成分子与分母,保证分子严格小于分母
denominators[0] = getNum(1, 66);
numerators[0] = getNum(1, max(denominators[0] - 22, 1));
int g = __gcd(numerators[0], denominators[0]);
numerators[0] /= g, denominators[0] /= g;
int nowNume = numerators[0], nowDeno = denominators[0];
for(int i = 1; i < operandNum; ++i){
denominators[i] = getNum(1, 66);
numerators[i] = getNum(1, max(denominators[0] - 22, 1));
g = __gcd(numerators[i], denominators[i]);
numerators[i] /= g, denominators[i] /= g;
g = __gcd(denominators[i], nowDeno);
int lcm = nowDeno*denominators[i]/g;
nowNume *= denominators[i]/g;
if(operators[i - 1] == 10){
nowNume += numerators[i]*nowDeno/g;
if(nowNume >= nowDeno) return false;
}else if(operators[i - 1] == 11){
nowNume -= numerators[i]*nowDeno/g;
if(nowNume <= 0) return false;
}
nowDeno = lcm;
g = __gcd(nowNume, nowDeno);
nowNume /= g, nowDeno /= g;
// 当运算过程中分子分母大于阈值(这里是 666 ),则重新生成算式
if(nowNume > 666 || nowDeno > 666) return false;
}
printf("%d/%d", numerators[0], denominators[0]);
for(int i = 1; i < operandNum; ++i){
printf("%c", op[operators[i - 1]]);
printf("%d/%d", numerators[i], denominators[i]);
}
printf("=%d/%d\n", nowNume, nowDeno);
return true;
}
// 这里 x 与模数控制分数运算式出现的概率
// 这里限定为 30% 的分数运算式, 50% 的有括号普通运算式 ,20% 的无括号普通运算式
void solve(){
int x = getNum(0, 9);
if(x <= 2) while(!solve2()) ;
else if(x <= 7) while(!solve1(true)) ;
else while(!solve1(false)) ;
}
int main(){
// 初始化函数零:初始化运算符 id 符号 以及运算优先级,并且重定向输出
init0();
//读入需要生成的运算式数量
int n;
scanf("%d", &n);
//输出学号及运算式
puts("2017012449");
while(n--) solve();
return 0;
}
1 生成n道数字在 0~100 之间的算术题
2 运算过程中不出现分数与负数
3 运行 .exe 生成 result.txt
4 支持有括号的运算
重新复审了代码
优化了代码格式
分段加入了注释
修复了一些小BUG
解决了被除数可能会大于100的问题
成功生成真分数运算式
封装了随机数生成函数
随机生成无括号运算式