结对编程——C++实现含括号的四则运算练习程序
一、问题设计
原问题
小学老师要每周给同学出300道四则运算练习题,由同学输入答案并判断对错,如果答案正确,则输出“回答正确”,否则输出“回答错误”,并给出正确答案。每道题有两个运算符,数字在100以内。答案在0~1000之间。
问题扩展
我们组将问题扩展到了带括号的四则运算练习题,每道题的运算数数量在[3,7]之间随机选择,运算符也由程序随机生成,括号也同样由程序随机插入,最终得到的式子作为练习题,由同学输入答案并判断对错,如果答案正确,则输出“回答正确”,否则输出“回答错误”,并给出正确答案。
二、算法思路
解决该问题需要解决以下子问题:
- 如何随机生成运算数、运算符?
- 如何拼组成四则运算式?
- 如何随机插入括号,使得括号完整且有意义?
- 如何让程序按正确运算顺序进行运算?
子问题 1 - 如何随机生成运算数、运算符?
使用rand()函数随机生成0 ~ 20的随机整数,并对该整数加1,使得随机数在1~21范围之间,这避免了后续随机运算符操作中,出现0在分母上的情况。将该操作循环2100次,即300*7次,将结果存入数组,表示最大需要用到的运算数数组。
使用rand()函数随机生成0 ~ 3的整数,对应+、-、*、\的字符数组下标,用于随机生成运算符,将该操作也循环2100次,将结果存入数组,表示最大需要用到的运算符数组。
至此,我们就拥有了程序随机生成的运算数、运算符。
子问题 2 - 如何拼组成四则运算式?
使用rand()函数随机生成0 ~ 4的随机整数,并对该整数加3,使得随机数在3 ~ 7范围之间,作为四则运算式的运算数个数。
对该操作执行300次,每次循环内嵌套一个循环,遍历运算数数组和运算符数组,假设生成的随机运算数个数为i,那么取运算数数组当前位置后i个,和运算符数组当前位置后i-1个,插入到两个二维数组Shuzi、Fuhao中。这样,每次外循环都会取i个运算数和i-1个运算符,作为两个二维数组的某一行。
遍历两个二维数组,新建一个String类型的数组,将运算数和运算符交替插入到String数组的行String上。在插入过程中需要将运算数转换为字符串类型。
至此,我们就拥有了随机运算数个数的300个四则运算式,它们是String类型的,存在一个String类型数组中,这个数组的每一个元素都存放着一个String四则运算式。
子问题 3 - 如何随机插入括号,使得括号完整且有意义?
在生成四则运算式的过程中,对每一个式子循环尝试100次为表达式加括号,
0: 在数字前加左括号 (,
1: 在数字后加右括号 ),
2: 不加括号
若多次尝试失败,生成无括号的保底算式。
至此,我们就得到了经过随机插入括号操作的括号四则运算式。
子问题 4 - 如何让程序按正确运算顺序进行运算?
带括号的四则运算式运算顺序如下:
先乘除、后加减,如果有括号则先算括号内的式子。
很容易联想到数据结构栈的经典例题——括号表达式。
其算法思路如下:
新建一个运算数栈和一个运算符栈,遍历括号表达式。如果遇到数字字符串,则将数字字符串转化为数字类型,存放到运算数栈中。如果遇到运算符字符,则存放到运算符栈中。
如果运算符栈顶是乘*或除/,取运算数栈顶两个元素并弹出,进行乘或除操作,将结果插入到运算数栈中,弹出运算符栈顶元素。
如果运算符栈顶是右括号),则必然在此前插入过左括号(。新建临时运算数栈和临时运算符栈,从右括号处出发,遍历到第一个栈中左括号的位置停下,将经过的i个运算符和i+1个运算数分别存入临时运算数栈和临时运算符栈中,实现倒置操作。接下来对临时栈进行运算,并将结果插入到运算数栈中。
遍历结束后,倒置运算数栈和运算符栈,以对剩余的运算从左到右按顺序计算。
当运算符栈空时,运算数栈剩余的数字就是括号表达式的答案。
三、程序代码
头文件与命名空间
#include<bits/stdc++.h>
using namespace std;
运算数个数随机生成函数
int generateRandomN() {
return rand() % 5 + 3;
}
运算数与运算符随机生成函数
void processData(vector<vector<int> >& Shuzi, vector<vector<char> >& Fuhao) {
int num[2100];
char fuhao[2100];
const char operators[] = {'+', '-', '*', '/'};
for (int i = 0; i < 2100; ++i) {
num[i] = rand() % 21 + 1;
}
for (int i = 0; i < 2100; ++i) {
fuhao[i] = operators[rand() % 4];
}
int numIndex = 0, fuhaoIndex = 0;
for (int i = 0; i < 300; ++i) {
int n = generateRandomN();
vector<int> currentNumbers;
for (int j = 0; j < n; ++j) {
currentNumbers.push_back(num[numIndex++]);
}
Shuzi.push_back(currentNumbers);
vector<char> currentOperators;
for (int j = 0; j < n - 1; ++j) {
currentOperators.push_back(fuhao[fuhaoIndex++]);
}
Fuhao.push_back(currentOperators);
}
}
括号表达式组合函数
void generateExpressions(const vector<vector<int> >& Shuzi, const vector<vector<char> >& Fuhao, vector<string>& Suanshi) {
for (int i = 0; i < Shuzi.size(); ++i) {
const vector<int>& numbers = Shuzi[i];
const vector<char>& ops = Fuhao[i];
string expr;
bool valid = false;
int attempts = 0;
const int max_attempts = 100;
while (!valid && attempts < max_attempts) {
expr.clear();
int current_left = 0;
for (int j = 0; j < numbers.size(); ++j) {
if (j > 0) {
expr += ops[j-1];
}
int action = rand() % 3;
if (action == 0) {
expr += '(';
expr += to_string(numbers[j]);
current_left++;
} else if (action == 1 && current_left > 0) {
expr += to_string(numbers[j]);
expr += ')';
current_left--;
} else {
expr += to_string(numbers[j]);
}
}
valid = (current_left == 0);
attempts++;
}
if (!valid) {
expr.clear();
for (int j = 0; j < numbers.size(); ++j) {
if (j > 0) {
expr += ops[j-1];
}
expr += to_string(numbers[j]);
}
}
Suanshi.push_back(expr);
}
}
表达式运算函数
double cal(string s){
stack<double> snum;
stack<char> sope;
for(int i = 0; i < s.size(); ++i){
if(!(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '(' || s[i] == ')')){
int ind = i;
double x = 0;
while(ind < s.size() && s[ind] >= '0' && s[ind] <= '9'){
x += s[ind] - '0';
x *= 10;
ind += 1;
}
x /= 10;
snum.push(x);
i = ind - 1;
}
if(s[i] == '('){
sope.push(s[i]);
continue;
}
if(!sope.empty() && sope.top() == '*'){
sope.pop();
double a = snum.top();
snum.pop();
double b = snum.top();
snum.pop();
snum.push(a * b);
}
else if(!sope.empty() && sope.top() == '/'){
sope.pop();
double a = snum.top();
snum.pop();
double b = snum.top();
snum.pop();
snum.push(b / a);
}
if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '(' || s[i] == ')'){
sope.push(s[i]);
}
if(!sope.empty() && sope.top() == ')'){
sope.pop();
stack<double> snumt;
stack<char> sopet;
while(sope.top() != '('){
sopet.push(sope.top());
sope.pop();
snumt.push(snum.top());
snum.pop();
}
snumt.push(snum.top());
snum.pop();
sope.pop();
while(!sopet.empty()){
char x = sopet.top();
sopet.pop();
double a = snumt.top();
snumt.pop();
double b = snumt.top();
snumt.pop();
if(x == '+'){
snumt.push(a + b);
}
else if(x == '*'){
snumt.push(a * b);
}
else if(x == '/'){
snumt.push(a / b);
}
else{
snumt.push(a - b);
}
}
snum.push(snumt.top());
snumt.pop();
}
}
stack<double> snum2;
stack<char> sope2;
while(!snum.empty()){
snum2.push(snum.top());
snum.pop();
}
while(!sope.empty()){
sope2.push(sope.top());
sope.pop();
}
while(!sope2.empty()){
char x = sope2.top();
sope2.pop();
double a = snum2.top();
snum2.pop();
double b = snum2.top();
snum2.pop();
if(x == '+'){
snum2.push(a + b);
}
else if(x == '-'){
snum2.push(a - b);
}
else if(x == '*'){
snum2.push(a * b);
}
else{
snum2.push(a / b);
}
}
return snum2.top();
}
主函数
int main() {
srand(time(0));
vector<vector<int> > Shuzi;
vector<vector<char> > Fuhao;
vector<string> Suanshi;
processData(Shuzi, Fuhao);
generateExpressions(Shuzi, Fuhao, Suanshi);
for(int i = 0; i < Suanshi.size(); ++i){
cout << Suanshi[i] << endl;
cout << "请回答:(四舍五入到4位小数)" << endl;
double ans = cal(Suanshi[i]);
double x;
cin >> x;
if(abs(x - ans) < 0.0001){
cout << "回答正确!" << endl;
}
else{
cout << "回答错误!正确答案是:" << endl << ans << endl;
}
}
return 0;
}
四、运算结果

五、作业体会
通过此次结对编程作业,我们熟悉了实际开发中结对实现项目的过程,丰富了项目经验,提高了彼此的默契和沟通、合作能力。并且,我们也对学习过的知识、算法进行了应用,强化了我们的编程能力、问题建模能力。
在对原问题进行扩展时,也强化了我们的创新能力和解决问题的能力。
六、参与人员
学号:2352911
学号:2352926
浙公网安备 33010602011771号