结对作业——随机生成四则运算

结对作业——随机生成四则运算

Core组:陈修远、尹宇飞
Github地址:https://github.com/Yinyf0804/homework2

编程要求

函数要求

写一个能自动生成小学四则运算题目并给出答案的命令行 “软件”,主要有一下两个三个函数构成。

  • Calc()

    这个Calc 函数接受字符串的输入(字符串里就是算术表达式,例如 “53.5”,“7/8 - 3/8 ”,“3 +90 0.3”等等),需要考虑各种异常因素。
  • Setting()

    在生成四则运算题目之前,需要对一系列属性进行设置,例如生成题目的数量,操作数的数量,题目中操作数的数值的范围,运算符的种类,(+-*/,是否支持真分数运算,是否支持小数运算,是否支持乘方运算……
  • Generate()

    进行一些设置之后,就可以开始生成所需要的题目了,且有以下限制:

    (1)生成的题目计算过程中不能产生负数

    (2)生成的题目中不能包括不必要的括号

    (3)程序一次运行生成的题目不能重复

对接要求

把四则运算的计算功能包装在一个模块中(这个模块可以是一个类 Class, 一个DLL,等等)

需求分析

(1)需要生成足够数量的不相同的题目

(2)题目必须满足各种需要

(3)容错率足够高,需要考虑不同可能的错误情况

(4)实现与UI组的对接

团队分工

陈修远同学负责代码的主要架构和部分函数编写,bug修复以及程序鲁棒性的维护

尹宇飞同学负责部分代码的具体实现和dll的生成,以及与UI组交流和对接。

编程思想

考虑到每一步的运算结果均不能为负数,而直接生成一个后缀表达式再一步一步算,若有负数则丢弃实在是不好实现且代价略高。因此我们在这里选择了如下的递归的方式:

生成一个由n个操作符构成的表达式并得到结果:{

​ 生成一个由i个运算符构成的表达式并得到结果

​ 生成一个由n – i - 1个运算符构成的表达式并得到结果

​ 随机生成运算符将上述表达式合并,并计算结果

}

而对于整个表达式对于加法交换律和乘法交换律不重复的规定,我们在每次生成子表达式的过程中都同时生成一个标准化的表达式:

  • 即若碰到了可交换的运算,则将运算符左右两边的子表达式按ascii字符的顺序排序,并在最外面套一个括号,由此再根据一张哈希表来判断是否有重复的表达式生成。

代码架构与实现

基于上述编程的思想,我们大致写了一个框架出来。

由于要生成整数运算、分数运算和小数运算,而这三者之间很多地方都略有差异但是整体的框架并没有变。因此我们在这里考虑了写一个抽象类MyQuestion,中间提供一些虚函数的接口。

这些虚函数的具体实现则由继承这个抽象类的三个子类MyQuestionInInt, MyQuestionInFloat, MyQuestionInFraction(分别代表生成整数运算,小数运算和分数运算)来实现。

以上三个类都只能生成一个表达式及其答案,而最终生成一组表达式并判断是否重复,写文件等操作则由QuestionSetGenerator类调用以上三个类提供的接口来实现。

MyExpression结构体则用来存放表达式及其标准化形式、表达式的结果与表达式的运算符。

MyExpression结构体的成员如下:

struct MyExpression {            //一个表达式
    string expr;                 //表达
    string expr_normalized;      //标准化表达
    string expr_answer;          //答案
    char expr_oper;              //保存操作符
};

Myquestion类的声明如下:

class MyQuestion {
public:
    void GetQuestion(int operator_number, MyExpression& expression); 
    //获取一个问题/表达式
protected:
    vector<char> oper_set;//运算符集合
    pair<int, int> range; //数字的范围
    bool is_standard = true; //表达式是否符合规范
    void Clear(MyExpression& expression); //清空表达式
    void Combine(MyExpression& expression, MyExpression expression1, MyExpression expression2); //将两组表达式结合
    virtual bool HandleException(MyExpression& expression) = 0; //处理例外情况
    virtual string Calculate(string answer1, char oper, string answer2) = 0;
    //计算结果
    virtual string GetRandomNumber() = 0;
    //获取随机数字(的字符串形式)
};

可以看见,这里一共有三个虚函数

  • HandleException函数处理生成表达式过程中的一些例外情况
  • Calculate函数负责计算每一步的结果
  • GetRandomNumber是返回一个随机生成的数

以上三个函数对于整数运算、分数运算和小数运算均有差异,故写成了虚函数,而其他函数则都类似。

我在编程思想中描述的过程应该能在getQuestion函数中有所体现:

void MyQuestion::GetQuestion(int operator_number, MyExpression &expression) {
    if (operator_number == 0) {
        expression.expr_answer = GetRandomNumber();
        expression.expr = expression.expr_answer;
        expression.expr_normalized = expression.expr_answer;
        expression.expr_oper = ' ';
        return;
    }
    //运算符数量为0时随机生成数字
    else if (operator_number == 1) {
        bool is_exception = HandleException(expression);
        if (is_exception) {
            return;
        }
    }
    //运算符数量为1时的一些例外情况
    MyExpression expression1, expression2;
    while (true)
    {
        int key = (rand() % operator_number);               
        //随机取key值分割n个运算符
        Clear(expression1);
        Clear(expression2);
        GetQuestion(key, expression1); 
        //expression1 = getQuestion(key)
        GetQuestion(operator_number - key - 1, expression2);    
        //expression2 = getQuestion(num - key - 1)
        expression.expr_oper = oper_set[rand() % oper_set.size()];    
        //随机取运算符
        expression.expr_answer = Calculate(expression1.expr_answer, expression.expr_oper, expression2.expr_answer);    
        //计算结果
        if (is_standard) {
            break;
        }
    }
    Combine(expression, expression1, expression2);    
    //生成表达式, 通过operator判断是否需要加括号,并生成表达式的标准形式
}

至于更细节一些的地方,太多了,在此不一一赘述,文末附上代码。

六、本次组队的感想

  • 尹宇飞:

    由于陈修远同学在本次项目前已经对C++以及C++11的高级编程非常熟练,而我对C++的认识还只停留在起步阶段,所以一开始看到陈修远同学写的C++架构,还是有点担心自己能否完全理解并完成代码的编写,但这也给了我一个很好的深入学习C++的机会。

    随着项目的进行,我逐渐熟悉了C++语言的一些用法,也在陈修远同学的帮助下,成功完成了主要功能的编程内容,并在最后实现了与UI组的对接。虽然短短一个多星期,但这个项目进行的却比想象中的艰难一些,因为不仅我几乎从来没有实际编写过C++,而且期间层出不穷的bug和报错也让我非常头疼。

    然而,这个项目却让我受益匪浅,它不仅让我熟悉了C++的编写、dll的生成,还让我的自学能力有了质的飞越,同时,我也学会了许多如何与新队友沟通的技巧和方法,这对我之后的工作有着很大的帮助。虽然过程艰难,但与陈修远同学的合作还是很愉快的,同时,我很感谢这位编程高手这几天给我的帮助,他对编程的认知和对细节的一丝不苟的态度让我受益匪浅,很期待下次的合作。

  • 陈修远:

    这是我第一次尝试写虚函数和类的继承(讲道理我之前也是一无所知,都是假装用一个类封装一下装模作样写一写,里子还是函数式的编程思想),其实也算是第一次依葫芦画瓢的OO程序了,而且也不知道自己学得像不像。但第一次写抽象类,就感受到了它的强大。

    之前其实想过重载运算符这种操作,但好像实现代价略高而且对于整个程序并没有什么用处。模版类这种东西好像在这里也并不是特别适合(可能是我想不到怎么用吧),而虚函数这种东西是我在查找模版类资料的时候得知的,想了想好像能用就硬着头皮写了下去23333。

    当然程序肯定也有不足之处,我肯定也没有把方方面面都考虑周到,很多问题在跟UI组对接了之后才暴露出来,这说明了测试模块的必要性(然而我并不会,老师能讲一下么?)

    最后想说一下结对的心得吧,交流确实比想象中的困难,对方怎么想的他解释半天我也不见得就懂了,我怎么想的对方也听得迷迷糊糊的。如何才能更高效地交流,我也不是特别明白,所以如果我在交流过程中有表现出不耐烦,希望尹宇飞同学可以包容一下。至于效率,一个简简单单的函数可能自己写只要一两下,而对方却要花很久去看。(或者说我自己隔几天去看就也没心情看下去了)这也是个问题,如何解决也要靠之后慢慢摸索了。

PSP表格

PSP2.1任务内容计划需要完成的时间(min)实际需要完成的时间(min)
Estimate估算1515
Analysis需求分析135200
Design Spec设计文档1515
Coding Standard代码规范1010
Design具体设计60120
Coding具体编码300480
Code Review2 h包含在具体编码过程中/
Test测试120300
Record Time Spent记录用时1010
Test Report测试报告2040
Size Measurement计算工作量2010
Postmortem总结改进180180
Summary合计8851380

代码

MyExpression结构体

struct MyExpression {            //一个表达式
    string expr;                 //表达
    string expr_normalized;      //标准化表达
    string expr_answer;          //答案
    char expr_oper;              //保存操作符
};

MyQuestion类

class MyQuestion {
public:
    void GetQuestion(int operator_number, MyExpression& expression);
    //获取一个问题/表达式
protected:
    vector<char> oper_set; //运算符集合
    pair<int, int> range;  //数字的范围
    bool is_standard = true; //表达式是否符合规范
    void Clear(MyExpression& expression); //清空表达式
    void Combine(MyExpression& expression, MyExpression expression1, MyExpression expression2);
    //将两组表达式结合
    virtual bool HandleException(MyExpression& expression) = 0; //处理例外情况
    virtual string Calculate(string answer1, char oper, string answer2) = 0; //计算结果
    virtual string GetRandomNumber() = 0; //获取随机数字(的字符串形式)
};

void MyQuestion::GetQuestion(int operator_number, MyExpression &expression) {
    if (operator_number == 0) {
        expression.expr_answer = GetRandomNumber();
        expression.expr = expression.expr_answer;
        expression.expr_normalized = expression.expr_answer;
        expression.expr_oper = ' ';
        return;
    }
    else if (operator_number == 1) {
        bool is_exception = HandleException(expression);
        if (is_exception) {
            return;
        }
    }
    MyExpression expression1, expression2;
    while (true)
    {
        int key = (rand() % operator_number);               //get random number key in range(0,number-1)
        Clear(expression1);
        Clear(expression2);
        GetQuestion(key, expression1);              //expression1 = getQuestion(key)
        GetQuestion(operator_number - key - 1, expression2);    //expression2 = getQuestion(num - key - 1)
        expression.expr_oper = oper_set[rand() % oper_set.size()];    //get random operator
        expression.expr_answer = Calculate(expression1.expr_answer, expression.expr_oper, expression2.expr_answer);    //calc answer
        if (is_standard) {
            break;
        }
    }
    Combine(expression, expression1, expression2);    //form question //通过operator判断是否需要加括号
}

void MyQuestion::Clear(MyExpression& expression) {
    expression.expr_oper = '\0';
    expression.expr = "";
    expression.expr_answer = "";
    expression.expr_normalized = "";
}

void MyQuestion::Combine(MyExpression& expression, MyExpression expression1, MyExpression expression2) {
    char p = expression.expr_oper;
    if (p != '^' && p != '-' && p != '/' && expression1.expr_normalized > expression2.expr_normalized)      //小的排前面
        expression1.expr_normalized.swap(expression2.expr_normalized);
    expression.expr_normalized = '(';
    expression.expr_normalized += expression1.expr_normalized;
    expression.expr_normalized += expression.expr_oper;
    expression.expr_normalized += expression2.expr_normalized;
    expression.expr_normalized += ')';
    //combine normalised question
    if (expression.expr_oper == '^') {
        if (expression1.expr_oper != ' ') {
            expression.expr += '(';
            expression.expr += expression1.expr;
            expression.expr += ')';
        }
        else {
            expression.expr += expression1.expr;
        }
        expression.expr += expression.expr_oper;

        if (expression2.expr_oper != ' ') {
            expression.expr += '(';
            expression.expr += expression2.expr;
            expression.expr += ')';
        }
        else {
            expression.expr += expression2.expr;
        }
    }

    else if ((expression.expr_oper == '*') || (expression.expr_oper == '/')) {
        if (expression1.expr_oper == '+' || expression1.expr_oper == '-') {
            expression.expr += '(';
            expression.expr += expression1.expr;
            expression.expr += ')';
        }
        else {
            expression.expr += expression1.expr;
        }
        expression.expr += expression.expr_oper;

        if (expression2.expr_oper != ' ') {
            expression.expr += '(';
            expression.expr += expression2.expr;
            expression.expr += ')';
        }
        else {
            expression.expr += expression2.expr;
        }
    }
    else {
        expression.expr += expression1.expr;
        expression.expr += expression.expr_oper;
        if (expression2.expr_oper == '+' || expression2.expr_oper == '-') {
            expression.expr += '(';
            expression.expr += expression2.expr;
            expression.expr += ')';
        }
        else {
            expression.expr += expression2.expr;
        }
    }
    //combine question/expression
}

MyQuestionInInt类

//整数运算
class MyQuestionInInt : public MyQuestion {
public:
    MyQuestionInInt() {
        support_devision = true;
        support_exponentiation = true;
        oper_set.push_back('+');
        oper_set.push_back('-');
        oper_set.push_back('*');
        range.first = 0;
        range.second = 100;
    }
    MyQuestionInInt(vector<char> oper, pair<int, int> input_range) {
        support_devision = false;
        support_exponentiation = false;
        auto iter = find(oper.begin(), oper.end(), '/');
        if (iter != oper.end()) {
            oper.erase(iter);
            support_devision = true;
        }
        iter = find(oper.begin(), oper.end(), '^');
        if (iter != oper.end()) {
            oper.erase(iter);
            support_exponentiation = true;
        }
        oper_set = oper;
        range = input_range;
    }
private:
    bool support_devision; //是否支持除法
    bool support_exponentiation; //是否支持乘方
    bool isInRange(string a); //是否在范围内
    //以下函数继承至MyQuestion
    bool HandleException(MyExpression& expression); 
    string Calculate(string answer1, char oper, string answer2);
    string GetRandomNumber();
    string SqrtGetRandomNumber();
};


string MyQuestionInInt::SqrtGetRandomNumber() {
    int upper = 2 * sqrt(range.second);
    int lower = (sqrt(range.first) + 1) / 2 + 1;
    int num = rand() % (upper - lower) + lower;
    return to_string(num);
}//get random number in range (sqrt(lower)/2, sqrt(upper)*2)




string MyQuestionInInt::GetRandomNumber() {
    int num = rand() % (range.second - range.first) + range.first;
    return to_string(num);
}//get random number

bool MyQuestionInInt::HandleException(MyExpression& expression) {
    if (!support_devision && !support_exponentiation) {
        return false;
    }
    int key;
    if (oper_set.empty()) {//if there's no operator in operset
        if (!support_devision && support_exponentiation) {
            key = 1;
        }
        else if (support_devision && !support_exponentiation) {
            key = 2;
        }
        else {
            key = (rand() % 2) + 1;
        }
    }
    else { //there is other operators in operset
        if (!support_devision && support_exponentiation) {
            key = rand() % 2;
        }
        else if (support_devision && !support_exponentiation) {
            key = (rand() % 2) * 2;
        }
        else {
            key = rand() % 3;
        }
    }
    string a1, a2, a3;
    if (key == 0) {
        return false;
    }
    else if (key == 1) { //exponentiation
        while (true) {
            a1 = GetRandomNumber();
            if (stoi(a1) <= 50) {
                break;
            }
        }

        a2 = to_string(rand() % 5);
        expression.expr_oper = '^';
    }
    else { //devision
        while (true) {
            a2 = SqrtGetRandomNumber();
            a3 = SqrtGetRandomNumber();
            a1 = Calculate(a2, '*', a3);
            if (stoi(a1) <= range.second && stoi(a1) >= range.first) {
                break;
            }
        }
        expression.expr_oper = '/';
    }
    expression.expr_answer = Calculate(a1, expression.expr_oper, a2);
    expression.expr = a1;
    expression.expr += expression.expr_oper;
    expression.expr += a2;
    expression.expr_normalized = "(";
    expression.expr_normalized += expression.expr;
    expression.expr_normalized += ")";
    return true;
}

//calculate result
string MyQuestionInInt::Calculate(string answer1, char oper, string answer2) {
    string num;
    int number, num1, num2;
    num1 = stoi(answer1);
    num2 = stoi(answer2);
    is_standard = true;
    if (oper == '+') {
        number = num1 + num2;
    }
    else if (oper == '-') {
        number = num1 - num2;
    }
    else if (oper == '*') {
        number = num1 * num2;
    }
    else if (oper == '/') {
        number = num1 / num2;
    }
    else if (oper == '^') {
        number = pow(num1, num2);
    }
    else {
        is_standard = false;
        return "-1";
    }
    if (number < 0) {
        is_standard = false;
        return "-1";
    }
    num = to_string(number);
    return num;
}

MyQuestionInFloat类

//小数运算,与上面类似
class MyQuestionInFloat : public MyQuestion {
public:
    MyQuestionInFloat() {
        oper_set.push_back('+');
        oper_set.push_back('-');
        oper_set.push_back('*');
        oper_set.push_back('/');
        range.first = 0;
        range.second = 100;
    }
    MyQuestionInFloat(vector<char> oper, pair<int, int> input_range) {
        oper_set = oper;
        range = input_range;
    }
private:
    bool HandleException(MyExpression& expression);
    string Calculate(string answer1, char oper, string answer2);
    string GetRandomNumber();
    //  double Round(double dVal, short iPlaces);
};


//no exception here
bool MyQuestionInFloat::HandleException(MyExpression &expression) {
    return false;
}

//calculate result
string MyQuestionInFloat::Calculate(string answer1, char oper, string answer2) {
    string num;
    double number, num1, num2;
    num1 = stod(answer1);
    num2 = stod(answer2);
    is_standard = true;
    if (oper == '+') {
        number = num1 + num2;
    }
    else if (oper == '-') {
        number = num1 - num2;
        if (number < 0) {
            is_standard = false;
        }
    }
    else if (oper == '*') {
        number = num1 * num2;
    }
    else if (oper == '/') {
        if (num2 == 0) {
            is_standard = false;
            return("-1");
        }
        number = num1 / num2;
    }
    else {
        number = -1;
    }
    /*      else if (oper == '^') {
    number = pow(num1, num2);
    }
    */
    //  num = to_string(Round(number, 2));
    //  num = num.substr(0, num.size() - 4);
    num = to_string(number);
    return num;
}

// get random number
string MyQuestionInFloat::GetRandomNumber() {
    int random = rand() % (range.second * 10 - range.first * 10) + range.first * 10;
    double number = double(random) / 10;
    string num = to_string(number);
    return(num.substr(0, num.size() - 5));
}

MyQuestionInFraction类

//分数运算
class MyQuestionInFraction : public MyQuestion {
public:
    MyQuestionInFraction() {
        oper_set.push_back('+');
        oper_set.push_back('-');
        oper_set.push_back('*');
        oper_set.push_back('/');
        range.first = 0;
        range.second = 100;
    }
    MyQuestionInFraction(vector<char> oper, pair<int, int> input_range) {
        oper_set = oper;
        range = input_range;
    }
private:
    bool HandleException(MyExpression& expression);
    string Calculate(string answer1, char oper, string answer2);
    string GetRandomNumber();
    void reduction(int a[], string& ans);
    void split(string src[], string str);
};



//get random number
string MyQuestionInFraction::GetRandomNumber() {
    int num = rand() % (range.second - range.first) + range.first;
    return to_string(num);

}

//split string (a/b) to (a,b)
void MyQuestionInFraction::split(string src[], string str) {
    //int size = str.size();
    int i = str.find("/");
    if (i == -1) {
        src[0] = str;
        src[1] = "1";
    }
    else {
        src[0] = str.substr(0, i);
        src[1] = str.substr(i + 1);
    }
}

//reduction of a fraction number
void MyQuestionInFraction::reduction(int a[], string& ans) {
    if (a[0] == 0) {
        ans = "0";
        return;
    }
    if (a[1] == 0) {
        ans = "-1";
        is_standard = false;
        return;
    }
    if (a[0] < 0 || a[1] < 0) {
        ans = "-1";
        is_standard = false;
        return;
    }
    int max, min;
    if (a[0] > a[1]) {
        max = a[0];
        min = a[1];
    }
    else {
        max = a[1];
        min = a[0];
    }
    while (max % min != 0) {
        int temp = max % min;
        max = min;
        min = temp;
    }
    int up = a[0] / min, down = a[1] / min;
    if (down == 1) {
        ans = to_string(up);
    }
    else {
        ans = to_string(up);
        ans += "/";
        ans += to_string(down);
    }
}

// calculate result
string MyQuestionInFraction::Calculate(string answer1, char oper, string answer2) {
    string num, a[2], b[2];
    split(a, answer1);
    split(b, answer2);
    int answer[2];
    int a0 = stoi(a[0]), a1 = stoi(a[1]), b0 = stoi(b[0]), b1 = stoi(b[1]);
    is_standard = true;

    if (oper == '+') {
        answer[0] = a0 * b1 + a1 * b0;
        answer[1] = a1 * b1;
    }
    else if (oper == '-') {
        answer[0] = a0 * b1 - a1 * b0;
        answer[1] = a1 * b1;
    }
    else if (oper == '*') {
        answer[0] = a0 * b0;
        answer[1] = a1 * b1;
    }
    else if (oper == '/') {
        if (stoi(b[0]) == 0) {
            is_standard = false;
            return num;
        }
        answer[0] = a0 * b1;
        answer[1] = a1 * b0;
    }
    reduction(answer, num);
    return num;
}


//no exception here
bool MyQuestionInFraction::HandleException(MyExpression& expression) {
    return false;
}

QuestionSetGenerator类

class __declspec(dllexport)  QuestionSetGenerator {

public:
    int ques_num;                        //题目的数量
    int oper_num;                        //一个表达式中运算符的数量
    vector<char> oper_set;               //运算符的种类
    int mode;                            //0为整数,1为小数,2为分数
    pair<int, int> input_range;          //题目数值的范围
    double Round(double dVal, short iPlaces);
    QuestionSetGenerator();
    int Setting(int question_number, int operator_number, string oper, int question_type, int lower_bound, int upper_bound);                   //读文件并更改设置
    void Generate();                     //产生题目并写文件
    bool flag;
};



QuestionSetGenerator::QuestionSetGenerator() {
    ques_num = 10;
    oper_num = 3;
    oper_set.push_back('+');
    oper_set.push_back('-');
    oper_set.push_back('*');
    oper_set.push_back('/');
    oper_set.push_back('@');
    mode = 0;
    input_range.first = 0;
    input_range.second = 100;
}

double  QuestionSetGenerator::Round(double dVal, short iPlaces) {
    double dRetval;
    double dMod = 0.0000001;
    if (dVal<0.0) dMod = -0.0000001;
    dRetval = dVal;
    dRetval += (5.0 / pow(10.0, iPlaces + 1.0));
    dRetval *= pow(10.0, iPlaces);
    dRetval = floor(dRetval + dMod);
    dRetval /= pow(10.0, iPlaces);
    return(dRetval);
}

int QuestionSetGenerator::Setting(int quesnum, int opernum, string oper, int question_type, int lower_bound, int upper_bound) {
    ques_num = 10;
    oper_num = 3;
    oper_set.push_back('+');
    oper_set.push_back('-');
    oper_set.push_back('*');
    oper_set.push_back('/');
    mode = 1;
    input_range.first = 0;
    input_range.second = 100;
    flag = true;
    if (quesnum <= 0 || opernum <= 0) {//question number and operator number should > 0
        flag = false;
        return -1;
    }
    if (question_type < 0 || question_type>2) { //question type should be 0,1 or 2
        flag = false;
        return -1;
    }
    int key[5] = { 0,0,0,0,0 };
    for (int i = 0; i < oper.size(); i++) {
        if (oper[i] == '+') {
            key[0] = 1;
        }
        else if (oper[i] == '-') {
            key[1] = 1;
        }
        else if (oper[i] == '*') {
            key[2] = 1;
        }
        else if (oper[i] == '/') {
            key[3] = 1;
        }
        else if (oper[i] == '^' && question_type == 0) {//only in mode 0 would ^ be allowed to appear
            key[4] = 1;
        }
    }
    if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0 && key[4] == 0) { //no supporting operator is not allowed
        flag = false;
        return -1;
    }
    if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0 && key[4] == 1 && opernum > 1 && question_type == 0) {
        flag = false;
        return -1;
    }
    //if only ^ is supported in mode 0, then operator number should be 1
    if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 1 && key[4] == 0 && opernum > 1 && question_type == 0) {
        flag = false;
        return -1;
    }
    //if only / is supported in mode 0, then operator number should be 1
    if (lower_bound >= upper_bound) { //lower bound should not be larger than upper bound
        flag = false;
        return -1;
    }
    if (lower_bound < 0) { //lower bound should > 0
        flag = false;
        return -1;
    }

    ques_num = quesnum;
    oper_num = opernum;
    mode = question_type;
    oper_set.clear();
    if (key[0] == 1) {
        oper_set.push_back('+');
    }
    if (key[1] == 1) {
        oper_set.push_back('-');
    }
    if (key[2] == 1) {
        oper_set.push_back('*');
    }
    if (key[3] == 1) {
        oper_set.push_back('/');
    }
    if (key[4] == 1) {
        oper_set.push_back('^');
    }
    input_range.first = lower_bound;
    input_range.second = upper_bound;
    return 0;
}

void QuestionSetGenerator::Generate() {
    unordered_map<string, bool> expression_set;
    if (flag == false) cout << "Sorry,you have made some stange requests" << endl;
    else {
        MyQuestion* ques;
        if (mode == 0) {
            //ques = new MyQuestionInInt();
            ques = new MyQuestionInInt(oper_set, input_range);
        }
        else if (mode == 1) {
            //ques = new MyQuestionInFloat;
            ques = new MyQuestionInFloat(oper_set, input_range);
        }
        else {
            //ques = new MyQuestionInFraction;
            ques = new MyQuestionInFraction(oper_set, input_range);
        }
        srand((unsigned)time(0));
        unsigned long key = 0;
        ofstream formula("formula.txt");
        ofstream result("result.txt");
        if (!formula.is_open())
        {
            cout << "Cannot create the file" << endl;
        }
        if (!result.is_open())
        {
            cout << "Cannot create the file" << endl;
        }

        for (int i = 0; i < ques_num; i++) {
            key++;
            if (key > 1000000) {
                formula << "Cannot generate so many questions with given condition." << endl;
                result << "Cannot generate so many questions with given condition." << endl;
            }
            MyExpression temp;
            ques->GetQuestion(oper_num, temp);             //获取一个问题
            auto iter = expression_set.find(temp.expr_normalized);
            if (iter == expression_set.end()) {            //若该问题没有重复
                expression_set[temp.expr_normalized] = true;       //存入map
                if (mode == 1)
                {
                    temp.expr_answer = to_string(Round(stod(temp.expr_answer), 2));
                    temp.expr_answer = temp.expr_answer.substr(0, temp.expr_answer.size() - 4);
                }
                if (mode == 2) {
                    int i = temp.expr_answer.find("/");
                    if (i != -1) {
                        string answer;
                        int num1 = stoi(temp.expr_answer.substr(0, i));
                        int num2 = stoi(temp.expr_answer.substr(i + 1));
                        if (num1 > num2)
                        {
                            answer = to_string(num1 / num2);
                            answer += '+';
                            answer += to_string(num1%num2);
                            answer += '/';
                            answer += to_string(num2);
                            temp.expr_answer = answer;
                        }
                    }
                }
                cout << temp.expr << " = " << temp.expr_answer << endl;
                    formula << temp.expr << endl;
                    result << temp.expr_answer << endl;
                //write file
            }
            else {                                   //若已存在这个问题
                i--;                                //这次循环不算数
            }
        }
        formula.close();
        result.close();
    }
}

转载于:https://www.cnblogs.com/maple66/p/8850493.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值