结对编程——C++实现含括号的四则运算练习程序

一、问题设计

原问题

小学老师要每周给同学出300道四则运算练习题,由同学输入答案并判断对错,如果答案正确,则输出“回答正确”,否则输出“回答错误”,并给出正确答案。每道题有两个运算符,数字在100以内。答案在0~1000之间。
问题扩展
我们组将问题扩展到了带括号的四则运算练习题,每道题的运算数数量在[3,7]之间随机选择,运算符也由程序随机生成,括号也同样由程序随机插入,最终得到的式子作为练习题,由同学输入答案并判断对错,如果答案正确,则输出“回答正确”,否则输出“回答错误”,并给出正确答案。

二、算法思路

解决该问题需要解决以下子问题:

  1. 如何随机生成运算数、运算符?
  2. 如何拼组成四则运算式?
  3. 如何随机插入括号,使得括号完整且有意义?
  4. 如何让程序按正确运算顺序进行运算?

子问题 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

posted @ 2025-04-18 00:45  羲祐  阅读(111)  评论(0)    收藏  举报