结对项目:实现自动生成小学四则运算题目

发布时间 2023-09-28 14:02:50作者: Youinho
软件工程 计科21级2班
作业要求 结对项目-实现四则运算题目生成
作业目标 合作完成四则运算题目生成项目

成员信息

姓名 学号
杨恒 3121005146
游烽 3121005148

Github:https://github.com/wcng010/SoftwareWork2

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 30
Estimate 估计这个任务需要多少时间 20 20
Development 开发 480 480
Analysis 需求分析 (包括学习新技术) 120 150
Design Spec 生成设计文档 30 30
Design Review 设计复审 30 30
Coding Standard 代码规范 (为目前的开发制定合适的规范) 20 20
Design 具体设计 20 5
Coding 具体编码 180 300
Code Review 代码复审 20 5
Test 测试(自我测试,修改代码,提交修改) 30 30
Reporting 报告 30 10
Test Repor 测试报告 20 10
Size Measurement 计算工作量 10 5
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 10 20
合计 1040 1145

环境:c++

设计实现过程

设计思路

1.运算式生成使用随机数和随机运算符,需要考虑除法的特殊性、运算符的个数和运算式重复等约束。
2.计算采用基于优先级的四则运算规则,根据括号优先、乘除优先和加减顺序的优先级规则进行运算。
3.文件存取通过调用现有的接口实现。

主要函数

  • 自定义运算类
namespace Mymath {
	inline int gcd(int a, int b)
	{
		return b == 0 ? a : gcd(b, a % b);
	}
	class MyFraction {
	private:
		int numerator;
		int denominator;
	public:
		explicit MyFraction(){}
		explicit MyFraction(const int num1,const int num2) :numerator(num1), denominator(num2) {}
		//explicit MyFraction(const MyFraction& f) :numerator(f.numerator), denominator(f.denominator) {}
		MyFraction operator+(const MyFraction& f) const;
		MyFraction operator -(const MyFraction& f) const;
		MyFraction operator *(const MyFraction& f) const;
		MyFraction operator /(const MyFraction& f) const;
		std::string GetValue();
		float Get();
		int GetNumerator();
		int GetDenominator();
		void FractionReduction();
		bool IsPositive();
		virtual ~ MyFraction(); 
	};
}
  • 功能函数(字符串格式转换、判别运算式是否符合约束条件等)
std::string Mymath::MyFraction::GetValue()
float Mymath::MyFraction::Get()
int Mymath::MyFraction::GetNumerator()
int Mymath::MyFraction::GetDenominator()
void Mymath::MyFraction::FractionReduction()
bool Mymath::MyFraction::IsPositive()
  • 运算函数(加减乘除)
Mymath::MyFraction Mymath::MyFraction::operator +(const MyFraction& f) const
Mymath::MyFraction Mymath::MyFraction::operator -(const MyFraction& f) const
Mymath::MyFraction Mymath::MyFraction::operator *(const MyFraction& f) const
Mymath::MyFraction Mymath::MyFraction::operator /(const MyFraction& f) const

具体实现

  • 加减乘除运算

将运算过程封装到函数中,方便调用。

Mymath::MyFraction Mymath::MyFraction::operator +(const MyFraction& f) const
{
	MyFraction temp;
	temp.numerator = numerator * f.denominator + f.numerator * denominator;
	temp.denominator = denominator * f.denominator;
	return temp;
}

Mymath::MyFraction Mymath::MyFraction::operator -(const MyFraction& f) const
{
	MyFraction temp;
	temp.numerator = numerator * f.denominator - f.numerator * denominator;
	temp.denominator = denominator * f.denominator;
	return temp;
}

Mymath::MyFraction Mymath::MyFraction::operator*(const MyFraction& f) const
{
	MyFraction temp;
	temp.numerator = numerator * f.numerator;
	temp.denominator = denominator * f.denominator;
	return temp;
}

Mymath::MyFraction Mymath::MyFraction::operator/ (const MyFraction& f) const
{
	MyFraction temp;
	temp.numerator = numerator * f.denominator;
	temp.denominator = denominator * f.numerator;
	return temp;
}
  • 单元计算

单元计算模块的设计有三个方面的要求,分别是将完成运算的两个值从分数数组中删除,将结果添加进入数组、将完成运算的运算符删除和将完成运算的运算符优先级删除。

void Mymath::Arithmetic::CalculateSingle(const int& index1, const int& index2)
{
	MyFraction fraction;
	switch (operatorVec[index1][index2])
	{
	case add: 
		fraction = fractionVec[index1][index2] + fractionVec[index1][index2 + 1];
		break;
	case subtract: fraction = fractionVec[index1][index2] - fractionVec[index1][index2 + 1];
		break;
	case multiply: fraction = fractionVec[index1][index2] * fractionVec[index1][index2 + 1];
		break;
	case divide: fraction = fractionVec[index1][index2] / fractionVec[index1][index2 + 1];
		break;
	default:break;
	}

	//处理不符合要求的数据
	if (fraction.Get()<0 && WrongNum.size() == 0) { WrongNum.emplace_back(index1); }
	else if (fraction.Get()<0 && index1 != WrongNum[WrongNum.size() - 1]) { WrongNum.emplace_back(index1);}
	//移除计算后的数据
	std::vector<operatorType>::iterator it = operatorVec[index1].begin() + index2;
	operatorVec[index1].erase(it);
	std::vector<operatorPriority>::iterator it1 = operatorPriorityVec[index1].begin() + index2;
	operatorPriorityVec[index1].erase(it1);
	std::vector<MyFraction>::iterator it2 = fractionVec[index1].begin() + index2;
	fractionVec[index1].erase(it2, it2 + 2);
	std::vector<MyFraction>::iterator it3 = fractionVec[index1].begin() + index2;
	fractionVec[index1].insert(it3, fraction);
}
  • 数值提取
std::string Mymath::MyFraction::GetValue()
{
	//分母是
	/**if (denominator % numerator == 0 && numerator / denominator != 0)
		return std::to_string(numerator/denominator);
	//分母小于分子
	else if (denominator<numerator)
	{
		int left = numerator/denominator;
		int right = numerator % denominator;
		return std::to_string(left) + "'" + std::to_string(right) + "/" + std::to_string(denominator);
	}
	else
	{
		return std::to_string(numerator) + "/" + std::to_string(denominator);
	}*/
	//输出前先约分
	FractionReduction();
	//分子小于分母
	if (numerator < denominator)
	{
		return std::to_string(numerator) + "/" + std::to_string(denominator);
	}
	//分子大于分母且不成比例
	else if (numerator > denominator && numerator % denominator != 0 && denominator != 1)
	{
		return std::to_string(numerator / denominator) + "'" + std::to_string(numerator % denominator) + "/" + std::to_string(denominator);
	}
	else if (numerator % denominator == 0)
	{
		return std::to_string(numerator / denominator);
	}
	return " ";
}
  • 分数计算
void Mymath::MyFraction::FractionReduction()
{
	// 分子为0,则分母为1
	if (numerator == 0) denominator = 1;

	// 如果分母为负数,则分子分母变号
	if (denominator < 0)
	{
		numerator = -numerator;
		denominator = -denominator;
	}

	// 约分: 分子分母同时除以最大公约数
	int x = gcd(abs(numerator), abs(denominator));
	numerator /= x;
	denominator /= x;
}

性能分析和测试

性能分析

生成10道题目时


从图中看出,在生成题目数少的情况下,将计算结果进行输出的函数在程序运行时间中占比很高,进行计算的模块时间占比少,这是合理现象。

函数的调用次数统计。

生成1000道题目时


用于运算的几个函数时间占比高。程序运行速度快。

测试

对整体进行测试

1.输入参数生成运算式

2.输入答案

3.运算式和答案存储在txt文件中


4.统计结果输出到文件Grade.txt

项目小结

杨恒:在结对项目中,充分的交流可以加快项目的进展速度,少走弯路。在这次实践中,我也收获了一些合作的方法,其中一条就是在合作过程中要多沟通,因为如果缺少交流,就有可能会两个人做了重叠的工作。
游烽:当一个人在遇到问题时,及时与伙伴沟通,可以快速解决问题。通过这次项目,我发现在项目开发过程中,对已经实现的模块及时进行测试,而不是等所有模块的代码都完成后在进行测试,效率更高。