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

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

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

编程要求

函数要求

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

  • Calc()

    这个Calc 函数接受字符串的输入(字符串里就是算术表达式,例如 “5*3.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 估算 15 15
Analysis 需求分析 135 200
Design Spec 设计文档 15 15
Coding Standard 代码规范 10 10
Design 具体设计 60 120
Coding 具体编码 300 480
Code Review 2 h 包含在具体编码过程中 /
Test 测试 120 300
Record Time Spent 记录用时 10 10
Test Report 测试报告 20 40
Size Measurement 计算工作量 20 10
Postmortem 总结改进 180 180
Summary 合计 885 1380

代码

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();
	}
}
posted @ 2018-04-16 00:07  Maple666  阅读(555)  评论(1编辑  收藏  举报