/*加减法算式生成器 v2.0
**By obertys 2020/08/06
**使用说明:
** 定义ARITHMETIC_COUNT宏为需要打印的算式数量
** 定义NUMERAL_COUNT常量作为算式操作数个数
** 定义MIN_NUM及MAX_NUM常量作为生成算式操作数及结果值的取值范围(整数区间[MIN_NUM,MAX_NUM])
**注意事项:
** 允许MAX_NUM值及MIN_NUM值为负数,但必须满足MIN_NUM<MAX_NUM
** 若ARITHMETIC_COUNT值过大且(MAX_NUM-MIN_NUM)值过小且NUMERAL_COUNT值过小,则可能造成死循环
** 在生产环境中不建议NUMERAL_COUNT值大于5;在测试环境中不建议NUMERAL_COUNT值过大
**编译支持:
** C++11编译器
*/
#include<set>
#include<array>
#include<tuple>
#include<ctime>
#include<random>
#include<iostream>
#include<functional>
using namespace std;
#define ARITHMETIC_COUNT 100 //生成的算式个数
constexpr unsigned short NUMERAL_COUNT=4; //每条算式的数字个数
constexpr short MIN_NUM=10; //算式允许的最小值
constexpr short MAX_NUM=99; //算式允许的最大值
enum class Op:char {plus='+',minus='-'}; //定义加减法操作
static_assert(NUMERAL_COUNT>=2&&NUMERAL_COUNT<=10000,"非法的算式操作数个数"); //算式的数字个数必须在整数区间[2,10000]
static_assert(MAX_NUM>MIN_NUM,"必须满足MAX_NUM>MIN_NUM"); //否则造成死循环
int evaluation(const array<short,NUMERAL_COUNT>& numeral,const array<Op,NUMERAL_COUNT-1>& op) //返回"操作数数组+操作符数组"所表示的算式的结果值
{
int res=numeral.front(); //取第一个操作数为计算结果的初始值
for(unsigned short i=0; i<NUMERAL_COUNT-1; ++i)
if(op[i]==Op::plus)
res+=numeral[i+1]; //结果加上下一个操作数
else if(op[i]==Op::minus)
res-=numeral[i+1]; //结果减去下一个操作数
else
{
cerr<<"错误的枚举值:"<<static_cast<int>(op[i])<<endl;
throw -1;
}
return res;
}
int main(void)
{
/*初始化*/
using Arithmetic=tuple<array<short,NUMERAL_COUNT>,array<Op,NUMERAL_COUNT-1>,short>; //定义Arithmetic类型为"操作数数组+操作符数组+结果"
set<Arithmetic> AriSet; //存贮生成算式的集合
auto rand=bind(uniform_int_distribution<short>(MIN_NUM,MAX_NUM),default_random_engine(time(nullptr))); //定义伪随机数发生器(绑定以[MIN_NUM,MAX_NUM]为区间的标准分布+以当前时刻为种子值的默认引擎)
/*生成算式*/
while(AriSet.size()<ARITHMETIC_COUNT) //当集合中算式数量达到ARITHMETIC_COUNT时退出循环
{
array<short,NUMERAL_COUNT> numeralArray;
for(auto& x:numeralArray) //随机生成NUMERAL_COUNT个数值,作为算式操作数
x=rand();
array<Op,NUMERAL_COUNT-1> opArray;
for(auto& x:opArray) //随机确定(NUMERAL_COUNT-1)个算式操作
x=rand()%2==0?Op::plus:Op::minus;
int result=evaluation(numeralArray,opArray); //算式求值
if(result>MAX_NUM||result<MIN_NUM) //结果值必须在整数区间[MIN_NUM,MAX_NUM]中
continue;
AriSet.insert(Arithmetic(move(numeralArray),move(opArray),result)); //存贮当前算式及算式结果,若已有重复则忽略
}
/*打印*/
cout<<"算式:\n";
for(const auto& x:AriSet) //遍历集合,打印生成的算式
{
for(unsigned short i=0; i<NUMERAL_COUNT-1; ++i)
cout<<get<0>(x)[i]<<' '<<static_cast<char>(get<1>(x)[i])<<' ';
cout<<get<0>(x).back()<<" =\n";
}
cout<<"\n对应的答案:\n";
for(const auto& x:AriSet)
cout<<get<2>(x)<<endl;
return 0;
}