四则运算生成器

文章目录

  • 1 任务描述
    • 1.1 概述
    • 1.2 基本要求
    • 1.3 附加功能要求
  • 2 项目地址
  • 3 项目源代码
  • 4 更新版本
    • 4.1 源代码 2.0
    • 4.2 源代码 3.0
    • 4.3 源代码 4.0
    • 4.4 代码 5.0
  • 5 演示图片
  • 6 个人软件过程 PSP
    • 6.1 代码1.0 PSP
      • 6.1.1 已实现功能
      • 6.1.2 未完成功能
      • 6.1.3 PSP表格
    • 6.2 代码2.0 PSP
      • 6.2.1
      • 6.2.1 更新内容
      • 6.2.2 PSP表格
    • 6.3 代码3.0 PSP
      • 6.3.1 更新内容
      • 6.3.2 PSP表格
    • 6.4 代码4.0 PSP
      • 6.4.1 更新内容
      • 6.4.2 PSP表格
    • 6.5 代码5.0 PSP
      • 6.5.1 更新内容
      • 6.5.2 PSP表格

1 任务描述

1.1 概述

使用java或C/C++编程语言,独立完成一个3到5个运算符的四则运算练习的软件

1.2 基本要求

  • 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.3 附加功能要求

  • 1 支持有括号的运算式,包括出题与求解正确答案。注意,算式中存在的括号数必须大于2对,且不得超过运算符的个数。

  • 2 扩展程序功能支持真分数的出题与运算(只需要涵盖加减法即可),例如:1/6 + 1/8 + 2/3= 23/24。注意在实现本功能时,需支持运算时分数的自动化简,比如 1/2+1/6=2/3,而非4/6,且计算过程中与结果都须为真分数。

2 项目地址

个人博客

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

3 项目源代码

#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;
}

4 更新版本

4.1 源代码 2.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;
}

4.2 源代码 3.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;
}


4.3 源代码 4.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;
}


4.4 代码 5.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;
}

5 演示图片

四则运算生成器_第1张图片

四则运算生成器_第2张图片

6 个人软件过程 PSP

6.1 代码1.0 PSP

6.1.1 已实现功能

  • 1 生成n道数字在 0~100 之间的算术题

  • 2 运算过程中不出现分数与负数

  • 3 运行 .exe 生成 result.txt

  • 4 支持有括号的运算

6.1.2 未完成功能

  • 1 支持真分数的出题与运算

6.1.3 PSP表格

四则运算生成器_第3张图片

6.2 代码2.0 PSP

6.2.1

6.2.1 更新内容

  • 重新复审了代码

  • 优化了代码格式

  • 分段加入了注释

  • 修复了一些小BUG

6.2.2 PSP表格

四则运算生成器_第4张图片

6.3 代码3.0 PSP

6.3.1 更新内容

解决了被除数可能会大于100的问题

四则运算生成器_第5张图片
优化了代码框架

6.3.2 PSP表格

四则运算生成器_第6张图片

6.4 代码4.0 PSP

6.4.1 更新内容

成功生成真分数运算式

四则运算生成器_第7张图片

6.4.2 PSP表格

四则运算生成器_第8张图片

6.5 代码5.0 PSP

6.5.1 更新内容

  • 封装了随机数生成函数

  • 随机生成无括号运算式

四则运算生成器_第9张图片

6.5.2 PSP表格

四则运算生成器_第10张图片

你可能感兴趣的:(软件工程导论)