|
|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
#include "include/exam.hpp"
|
|
|
|
|
#include "include/exam.h"
|
|
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
@ -33,14 +33,15 @@ void ExamGenerator::append_history(const std::filesystem::path& history_path, co
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> ExamGenerator::generate_unique(Level level, int count, std::unordered_set<std::string>& history) {
|
|
|
|
|
// 生成不重复题目:按难度生成并去重,限制尝试次数避免极端情况
|
|
|
|
|
std::vector<std::string> ExamGenerator::GenerateUnique(Level level, int count, std::unordered_set<std::string>& history) {
|
|
|
|
|
std::vector<std::string> out;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
while ((int)out.size() < count && attempts < count * 50) {
|
|
|
|
|
std::string q;
|
|
|
|
|
if (level == Level::Primary) q = gen_primary();
|
|
|
|
|
else if (level == Level::Middle) q = gen_middle();
|
|
|
|
|
else q = gen_high();
|
|
|
|
|
if (level == Level::Primary) q = GenPrimary();
|
|
|
|
|
else if (level == Level::Middle) q = GenMiddle();
|
|
|
|
|
else q = GenHigh();
|
|
|
|
|
|
|
|
|
|
if (history.insert(q).second) {
|
|
|
|
|
out.push_back(q);
|
|
|
|
|
@ -50,7 +51,8 @@ std::vector<std::string> ExamGenerator::generate_unique(Level level, int count,
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::string join_ops(const std::vector<int>& nums, const std::vector<char>& ops) {
|
|
|
|
|
// 将数字与运算符拼接为表达式字符串
|
|
|
|
|
static std::string JoinOps(const std::vector<int>& nums, const std::vector<char>& ops) {
|
|
|
|
|
std::string s;
|
|
|
|
|
for (size_t i = 0; i < nums.size(); ++i) {
|
|
|
|
|
s += std::to_string(nums[i]);
|
|
|
|
|
@ -63,37 +65,45 @@ static std::string join_ops(const std::vector<int>& nums, const std::vector<char
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ExamGenerator::gen_primary() {
|
|
|
|
|
// 生成小学题目:2-3 个操作数的四则运算
|
|
|
|
|
// 生成小学题目:2-3 个操作数的四则运算
|
|
|
|
|
std::string ExamGenerator::GenPrimary() {
|
|
|
|
|
int n_ops = rand_int(rng_, 1, 2); // 2-3个操作数 => 1-2个操作符
|
|
|
|
|
std::vector<int> nums;
|
|
|
|
|
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 50));
|
|
|
|
|
static const char all_ops[] = {'+', '-', '*', '/'};
|
|
|
|
|
static const char kAllOps[] = {'+', '-', '*', '/'};
|
|
|
|
|
std::vector<char> ops;
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
|
|
|
|
std::string expr = join_ops(nums, ops);
|
|
|
|
|
// 适度加括号
|
|
|
|
|
if (nums.size() >= 3 && rand_int(rng_, 0, 1)) {
|
|
|
|
|
size_t pos = expr.find(' ');
|
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
|
expr = "(" + expr.substr(0, pos) + ")" + expr.substr(pos);
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[rand_int(rng_, 0, 3)]);
|
|
|
|
|
std::string s;
|
|
|
|
|
if (n_ops == 1) {
|
|
|
|
|
// 两个操作数:不加括号
|
|
|
|
|
s = std::to_string(nums[0]) + " " + std::string(1, ops[0]) + " " + std::to_string(nums[1]);
|
|
|
|
|
} else {
|
|
|
|
|
// 三个操作数:随机选择包裹首或末二元表达式
|
|
|
|
|
bool wrap_first = (rand_int(rng_, 0, 1) == 1);
|
|
|
|
|
if (wrap_first) {
|
|
|
|
|
s = "(" + std::to_string(nums[0]) + " " + std::string(1, ops[0]) + " " + std::to_string(nums[1]) + ")";
|
|
|
|
|
s += " " + std::string(1, ops[1]) + " " + std::to_string(nums[2]);
|
|
|
|
|
} else {
|
|
|
|
|
s = std::to_string(nums[0]) + " " + std::string(1, ops[0]) + " ";
|
|
|
|
|
s += "(" + std::to_string(nums[1]) + " " + std::string(1, ops[1]) + " " + std::to_string(nums[2]) + ")";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return expr + " = ?";
|
|
|
|
|
return s + " = ?";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ExamGenerator::gen_middle() {
|
|
|
|
|
// 生成初中题目:包含平方或开方的表达式
|
|
|
|
|
std::string ExamGenerator::GenMiddle() {
|
|
|
|
|
int n_ops = rand_int(rng_, 2, 3); // 3-4 操作数
|
|
|
|
|
std::vector<int> nums;
|
|
|
|
|
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
|
|
|
|
|
static const char all_ops[] = {'+', '-', '*', '/'};
|
|
|
|
|
static const char kAllOps[] = {'+', '-', '*', '/'};
|
|
|
|
|
std::vector<char> ops;
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[rand_int(rng_, 0, 3)]);
|
|
|
|
|
// 随机把一个数变成平方或开方
|
|
|
|
|
int idx = rand_int(rng_, 0, (int)nums.size() - 1);
|
|
|
|
|
bool use_sqrt = rand_int(rng_, 0, 1) == 1;
|
|
|
|
|
std::string expr = join_ops(nums, ops);
|
|
|
|
|
|
|
|
|
|
std::string expr = JoinOps(nums, ops);
|
|
|
|
|
// 找到替换位置(简单方式:直接重新拼接)
|
|
|
|
|
std::string s;
|
|
|
|
|
for (size_t i = 0; i < nums.size(); ++i) {
|
|
|
|
|
@ -110,26 +120,24 @@ std::string ExamGenerator::gen_middle() {
|
|
|
|
|
return s + " = ?";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ExamGenerator::gen_high() {
|
|
|
|
|
// 生成高中题目:带三角函数项并可能平方或开方
|
|
|
|
|
std::string ExamGenerator::GenHigh() {
|
|
|
|
|
int n_ops = rand_int(rng_, 3, 4); // 4-5 操作数
|
|
|
|
|
std::vector<int> nums;
|
|
|
|
|
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
|
|
|
|
|
static const char all_ops[] = {'+', '-', '*', '/'};
|
|
|
|
|
static const char kAllOps[] = {'+', '-', '*', '/'};
|
|
|
|
|
std::vector<char> ops;
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n_ops; ++i) ops.push_back(kAllOps[rand_int(rng_, 0, 3)]);
|
|
|
|
|
// 先基础表达式
|
|
|
|
|
std::string base;
|
|
|
|
|
for (size_t i = 0; i < nums.size(); ++i) {
|
|
|
|
|
base += std::to_string(nums[i]);
|
|
|
|
|
if (i < ops.size()) { base += ' '; base += ops[i]; base += ' '; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 至少一个三角函数,角度用 30/45/60/90 之一,使用 "deg" 标识角度
|
|
|
|
|
static const char* funcs[] = {"sin", "cos", "tan"};
|
|
|
|
|
static const int angles[] = {30, 45, 60, 90};
|
|
|
|
|
std::string tri = std::string(funcs[rand_int(rng_, 0, 2)]) + "(" + std::to_string(angles[rand_int(rng_, 0, 3)]) + "deg)";
|
|
|
|
|
|
|
|
|
|
static const int kAngles[] = {30, 45, 60, 90};
|
|
|
|
|
std::string tri = std::string(funcs[rand_int(rng_, 0, 2)]) + "(" + std::to_string(kAngles[rand_int(rng_, 0, 3)]) + "deg)";
|
|
|
|
|
// 随机把一个数替换为三角项
|
|
|
|
|
size_t replace_pos = rand_int(rng_, 0, (int)nums.size() - 1);
|
|
|
|
|
// 重建字符串并替换
|
|
|
|
|
@ -139,7 +147,6 @@ std::string ExamGenerator::gen_high() {
|
|
|
|
|
s += term;
|
|
|
|
|
if (i < ops.size()) { s += ' '; s += ops[i]; s += ' '; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 同时可能再加平方或开方
|
|
|
|
|
if (rand_int(rng_, 0, 1)) {
|
|
|
|
|
s = "(" + s + ")^2";
|