1 #1

Open
hnu202326010320 wants to merge 42 commits from develop into main

@ -1,2 +0,0 @@
# projectone

@ -0,0 +1,125 @@
# 数学试卷自动生成程序
一个在命令行运行的数学试卷生成器,支持小学/初中/高中三种难度,按用户维度避免题目重复。
## 目录
- [功能特点](#功能特点)
- [运行环境要求](#运行环境要求)
- [快速开始(推荐分发)](#快速开始推荐分发)
- [使用说明](#使用说明)
- [账户信息](#账户信息)
- [题目难度说明](#题目难度说明)
- [小学](#小学)
- [初中](#初中)
- [高中](#高中)
- [文件保存](#文件保存)
- [项目结构](#项目结构)
- [开发与构建IntelliJ IDEA](#开发与构建intellij-idea)
- [常见问题与排查](#常见问题与排查)
- [注意事项](#注意事项)
## 功能特点
- 支持三个教学阶段:小学、初中、高中
- 预设 9 个教师账户(每个阶段 3 个账户)
- 自动生成不重复的数学题目(以用户为维度)
- 按用户分类保存题目文件
- 支持类型切换功能(无需重新登录即可切换难度)
## 运行环境要求
- 已安装 Java推荐 JDK 18JDK 17 也可)。
- Windows 命令行环境PowerShell 或 CMD
## 快速开始(推荐分发)
将以下文件打包并发送给使用者:
- projecttest.jar已构建的可运行 JAR
- 运行数学试卷生成器.bat中文名启动脚本
使用方式:
1. 解压后保证 .bat 与 projecttest.jar 在同一文件夹。
2. 双击任意一个 .bat 即可启动;脚本会自动寻找 JAVA_HOME 或 PATH 中的 java.exe并以 UTF-8 输出。
3. 如未安装 Java脚本会给出提示请先安装 JDK 17/18 并配置环境变量。
## 使用说明
程序启动后将显示提示:
- 输入题目数量1030生成试卷
- 输入“切换为 小学/初中/高中”来切换难度;例如:切换为 初中;
- 输入 -1 退出当前用户并重新登录;
- 同一用户生成的题目不会重复。
## 账户信息
### 小学账户
- 用户名张三1密码123
- 用户名张三2密码123
- 用户名张三3密码123
### 初中账户
- 用户名李四1密码123
- 用户名李四2密码123
- 用户名李四3密码123
### 高中账户
- 用户名王五1密码123
- 用户名王五2密码123
- 用户名王五3密码123
## 题目难度说明
- 不同阶段的题目难度与运算比例不同:小学加减比例高,高中乘除比例高。
- 括号会根据是否改变运算顺序而决定是否出现。
### 小学
- 运算符:+、-、*、/
- 操作数15 个
- 数值范围1100
- 运算结果不会出现负数
### 初中
- 包含小学所有运算
- 新增:平方运算、开根号运算
- 题目中至少包含一个平方或开根号运算
- 题目中的开根号运算都是可以计算出来的
### 高中
- 包含初中所有运算
- 新增sin、cos、tan 三角函数
- 题目中至少包含一个三角函数运算
- 题目中的三角函数都是可以运算出来的
## 文件保存
- 生成的题目会保存在 `math_papers` 文件夹下
- 每个用户有独立的文件夹
- 文件名格式:`年-月-日-时-分-秒.txt`
- 每道题目有题号,题目之间空一行
## 项目结构
- META-INF/MANIFEST.MF清单文件Main-Class: MathPaperGenerator
- out/production/projecttest模块编译输出.class
- out/artifacts/projecttest_jar/projecttest.jar构建产物JAR
- run_math_paper_english.bat、运行数学试卷生成器.bat启动脚本。
- math_papers/:生成的试卷与历史记录(按用户分文件夹)。
## 开发与构建IntelliJ IDEA
1. 项目结构Ctrl+Alt+Shift+S
- Project SDK 设为 JDK 18或 17Project language level 与 Bytecode version 保持一致。
- Modules > Sources将 d:\projecttest 标记为 Sources Root.idea 与 math_papers 标记为 Excluded。
- Modules > Paths选择“继承项目编译输出路径”输出到 d:\projecttest\out。
2. 构建项目Build > Build/Rebuild Project确认 out/production/projecttest 下生成 .class。
3. 构件Artifactsprojecttest:jar
- 输出布局包含“模块输出projecttest”与 META-INFMANIFEST.MF
- Build > Build Artifacts > Build projecttest:jar生成 JAR 于 out/artifacts/projecttest_jar/。
4. 运行:
- 命令行:`java -jar d:\projecttest\out\artifacts\projecttest_jar\projecttest.jar`
- 或双击两个 .bat 启动脚本。
## 常见问题与排查
- 不支持的标志: 21JDK 与语言级别不匹配。将 Project SDK/Language level/Bytecode 统一到 18或 17
- 没有为模块指定输出路径:在 Modules > Paths 中启用“继承项目编译输出路径”。
- 没有主清单属性:确认 META-INF/MANIFEST.MF 存在并包含 `Main-Class: MathPaperGenerator`;构件输出布局包含清单与模块输出。
- 找不到主类:主类名必须与清单一致,且 .class 已编译到 out/production/projecttest。
## 注意事项
- 题目数量必须在 1030 之间
- 同一用户生成的题目不会重复
- 程序会自动创建必要的文件夹
- 支持中文界面,操作简单直观
如需重新打包分发版,直接使用 zip`MathPaperGenerator_release.zip`,包含 JAR 与两个启动脚本,解压即用。

@ -0,0 +1,125 @@
import java.text.DecimalFormat;
import java.util.Random;
/**
*
*/
public abstract class AbstractProblemGenerator {
protected final Random random;
protected final DecimalFormat df;
protected AbstractProblemGenerator(Random random, DecimalFormat df) {
this.random = random;
this.df = df;
}
/**
*
*/
public abstract MathProblem generate();
// 构建带括号的表达式(只包两个操作数)
protected String buildExpressionWithBrackets(int[] operands, String[] operators, int startPos) {
StringBuilder expression = new StringBuilder();
for (int i = 0; i < operands.length; i++) {
if (i == startPos) {
expression.append("(");
}
expression.append(operands[i]);
if (i == startPos + 1) {
expression.append(")");
}
if (i < operands.length - 1) {
expression.append(" ").append(operators[i]).append(" ");
}
}
return expression.toString();
}
// 计算带括号的表达式结果(只合并两个操作数)
protected double evaluateExpressionWithBrackets(int[] operands, String[] operators, int startPos) {
double bracketResult = applyOperator(operands[startPos], operands[startPos + 1], operators[startPos]);
int newOperandCount = operands.length - 1;
int[] newOperands = new int[newOperandCount];
String[] newOperators = new String[newOperandCount - 1];
for (int i = 0; i < startPos; i++) newOperands[i] = operands[i];
newOperands[startPos] = (int) bracketResult;
for (int i = startPos + 1; i < newOperandCount; i++) newOperands[i] = operands[i + 1];
for (int i = 0; i < newOperandCount - 1; i++) {
if (i < startPos) newOperators[i] = operators[i];
else newOperators[i] = operators[i + 1];
}
double result = newOperands[0];
for (int i = 0; i < newOperandCount - 1; i++) {
result = applyOperator(result, newOperands[i + 1], newOperators[i]);
}
return result;
}
/** 应用运算符(整数右操作数) */
protected double applyOperator(double left, int right, String operator) {
switch (operator) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
case "/": return right != 0 ? left / right : left; // 避免除以零
default: return left;
}
}
/** 计算三角函数值 */
protected double calculateTrigFunction(String function, int angle) {
double radians = Math.toRadians(angle);
switch (function) {
case "sin": return Math.sin(radians);
case "cos": return Math.cos(radians);
case "tan":
if (angle == 90) return Double.POSITIVE_INFINITY; // tan(90°)未定义
return Math.tan(radians);
default: return 0;
}
}
/** Double 版本的运算符应用(用于包含三角函数的计算) */
protected double applyOperatorDouble(double left, double right, String operator) {
switch (operator) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
case "/": return right != 0.0 ? left / right : left; // 避免除以零
default: return left;
}
}
/** 将 int 数组转 double 数组 */
protected double[] toDoubleArray(int[] arr) {
double[] out = new double[arr.length];
for (int i = 0; i < arr.length; i++) out[i] = arr[i];
return out;
}
/**
* 0 -1
*/
protected int findBracketStart(double[] values, String[] operators, double probability) {
if (values == null || operators == null) return -1;
if (values.length <= 3) return -1; // 至少 4 个操作数才有加括号的意义
if (random.nextDouble() >= probability) return -1;//有括号的情况为人为设置现在概率为0.3
int maxStart = values.length - 2; // 仅包两个操作数
int[] candidateStarts = new int[Math.max(0, maxStart + 1)];
int candidateCount = 0;
//只有改变运算顺序的括号才有意义
for (int s = 0; s <= maxStart; s++) {
boolean insideLow = "+".equals(operators[s]) || "-".equals(operators[s]);
boolean leftHigh = (s - 1) >= 0 && ("*".equals(operators[s - 1]) || "/".equals(operators[s - 1]));
boolean rightHigh = (s + 1) < operators.length && ("*".equals(operators[s + 1]) || "/".equals(operators[s + 1]));
if (insideLow && (leftHigh || rightHigh)) {
candidateStarts[candidateCount++] = s;
}
}
if (candidateCount == 0) return -1;
int candidate = candidateStarts[random.nextInt(candidateCount)];
double bracketResult = applyOperatorDouble(values[candidate], values[candidate + 1], operators[candidate]);//避免括号内结果为0
return (bracketResult != 0.0) ? candidate : -1;
}
}

@ -0,0 +1,67 @@
import java.text.DecimalFormat;
import java.util.Random;
public class ElementaryProblemGenerator extends AbstractProblemGenerator {
public ElementaryProblemGenerator(Random random, DecimalFormat df) {
super(random, df);
}
@Override
public MathProblem generate() {
// 小学题目要求:答案不得为负数。最多尝试若干次生成,若仍为负则回退到简单加法题。
int maxAttempts = 50;
for (int attempt = 0; attempt < maxAttempts; attempt++) {
int operandCount = random.nextInt(4) + 2; // 2-5个操作数
String[] operators = {"+", "-", "*", "/"};
int[] operands = new int[operandCount];
String[] selectedOperators = new String[operandCount - 1];
for (int i = 0; i < operandCount; i++) operands[i] = random.nextInt(100) + 1;
// 运算符选择(避免连乘/连除,调整概率)
String previousOperator = null;
double pPlus = 0.45, pMinus = 0.35, pTimes = 0.15, pDivide = 0.05;
for (int i = 0; i < operandCount - 1; i++) {
String operator;
if ("*".equals(previousOperator) || "/".equals(previousOperator)) {
double randValue = random.nextDouble();
double total = pPlus + pMinus;
operator = randValue < (pPlus / total) ? "+" : "-";
} else {
double randValue = random.nextDouble();
if (randValue < pPlus) operator = "+";
else if (randValue < pPlus + pMinus) operator = "-";
else if (randValue < pPlus + pMinus + pTimes) operator = "*";
else operator = "/";
}
selectedOperators[i] = operator;
previousOperator = operator;
}
// 括号选择
int startPos = findBracketStart(toDoubleArray(operands), selectedOperators, 0.3);
String expression;
double result;
if (startPos != -1) {
expression = buildExpressionWithBrackets(operands, selectedOperators, startPos);
result = evaluateExpressionWithBrackets(operands, selectedOperators, startPos);
} else {
StringBuilder expBuilder = new StringBuilder();
expBuilder.append(operands[0]);
result = operands[0];
for (int i = 0; i < operandCount - 1; i++) {
expBuilder.append(" ").append(selectedOperators[i]).append(" ").append(operands[i + 1]);
result = applyOperator(result, operands[i + 1], selectedOperators[i]);
}
expression = expBuilder.toString();
}
if (result >= 0) {
return new MathProblem(expression, df.format(result), "小学");
}
// 如果为负数,继续下一次尝试
}
// 回退:生成一个保证非负的简单加法题
int a = random.nextInt(100) + 1;
int b = random.nextInt(100) + 1;
String expression = a + " + " + b;
double result = a + b;
return new MathProblem(expression, df.format(result), "小学");
}
}

@ -0,0 +1,137 @@
import java.io.*;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
*
*/
public class FileManager {
private static final String BASE_DIR = "math_papers";
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
/**
*
* @param user
* @param problems
* @param difficulty
* @return
*/
public String savePaperToFile(User user, List<MathProblem> problems, String difficulty) throws IOException {
// 创建用户文件夹
String userDir = BASE_DIR + File.separator + user.getUsername();
Files.createDirectories(Paths.get(userDir));
// 生成文件名
String timestamp = LocalDateTime.now().format(formatter);
String fileName = timestamp + ".txt";
String filePath = userDir + File.separator + fileName;
// 写入题目到文件
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath, StandardCharsets.UTF_8))) {
writer.println("数学题目卷子");
writer.println("用户: " + user.getUsername());
writer.println("类型: " + difficulty);
writer.println("生成时间: " + timestamp.replace("-", "/").replace("-", ":"));
writer.println("题目数量: " + problems.size());
writer.println("=" + "=".repeat(50));
writer.println();
for (int i = 0; i < problems.size(); i++) {
writer.println((i + 1) + ". " + problems.get(i).toString());
writer.println(); // 每题之间空一行
}
}
return filePath;
}
/**
*
* @param username
* @return
*/
public Set<String> getUserHistoryProblems(String username) {
Set<String> historyProblems = new HashSet<>();
String userDir = BASE_DIR + File.separator + username;
try {
Path userPath = Paths.get(userDir);
if (!Files.exists(userPath)) {
return historyProblems;
}
// 遍历用户文件夹中的所有txt文件
Files.walk(userPath)
.filter(path -> path.toString().endsWith(".txt"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
for (String line : lines) {
// 提取题目表达式(格式:数字. 表达式 = ?
if (line.matches("^\\d+\\. .+ = \\?$")) {
String expression = line.substring(line.indexOf(". ") + 2, line.lastIndexOf(" = ?"));
historyProblems.add(expression);
}
}
} catch (IOException e) {
System.err.println("读取文件失败: " + path);
}
});
} catch (IOException e) {
System.err.println("访问用户目录失败: " + userDir);
}
return historyProblems;
}
/**
*
* @param username
* @param problem
* @return
*/
public boolean isDuplicateProblem(String username, MathProblem problem) {
Set<String> historyProblems = getUserHistoryProblems(username);
return historyProblems.contains(problem.getExpression());
}
/**
*
* @param user
* @param generator
* @param count
* @param difficulty
* @return
*/
public List<MathProblem> generateUniqueProblems(User user, MathProblemGenerator generator,
int count, String difficulty) {
List<MathProblem> problems = new ArrayList<>();
Set<String> historyProblems = getUserHistoryProblems(user.getUsername());
Set<String> currentProblems = new HashSet<>();
int attempts = 0;
int maxAttempts = count * 10; // 最多尝试次数,避免无限循环
while (problems.size() < count && attempts < maxAttempts) {
MathProblem problem = generator.generateProblem(difficulty);
String expression = problem.getExpression();
// 检查是否与历史题目或当前题目重复
if (!historyProblems.contains(expression) && !currentProblems.contains(expression)) {
problems.add(problem);
currentProblems.add(expression);
}
attempts++;
}
if (problems.size() < count) {
System.out.println("警告: 只生成了 " + problems.size() + " 道题目,可能存在重复限制。");
}
return problems;
}
}

@ -0,0 +1,85 @@
import java.text.DecimalFormat;
import java.util.Random;
public class HighSchoolProblemGenerator extends AbstractProblemGenerator {
public HighSchoolProblemGenerator(Random random, DecimalFormat df) {
super(random, df);
}
@Override
public MathProblem generate() {
int operandCount = random.nextInt(3) + 3; // 3-5 个操作数
double[] operandsVal = new double[operandCount];
String[] displayOperands = new String[operandCount];
String[] selectedOperators = new String[operandCount - 1];
// 生成基本操作数
for (int i = 0; i < operandCount; i++) {
int v = random.nextInt(100) + 1;
operandsVal[i] = v;
displayOperands[i] = String.valueOf(v);
}
// 运算符选择(更均衡,避免连乘/连除)
String previousOperator = null;
double pPlus = 0.35, pMinus = 0.25, pTimes = 0.25, pDivide = 0.15;
for (int i = 0; i < operandCount - 1; i++) {
String operator;
if ("*".equals(previousOperator) || "/".equals(previousOperator)) {
double randValue = random.nextDouble();
double total = pPlus + pMinus;
operator = randValue < (pPlus / total) ? "+" : "-";
} else {
double randValue = random.nextDouble();
if (randValue < pPlus) operator = "+";
else if (randValue < pPlus + pMinus) operator = "-";
else if (randValue < pPlus + pMinus + pTimes) operator = "*";
else operator = "/";
}
selectedOperators[i] = operator;
previousOperator = operator;
}
// 三角函数
int trigIndex = random.nextInt(operandCount);
String[] functions = {"sin", "cos", "tan"};
String function = functions[random.nextInt(functions.length)];
int[] angles = {0, 30, 45, 60, 90};
int angle = angles[random.nextInt(angles.length)];
if ("tan".equals(function) && angle == 90) angle = 60; // 避免 tan(90°)
double trigVal = calculateTrigFunction(function, angle);
operandsVal[trigIndex] = trigVal;
displayOperands[trigIndex] = function + angle;
// 括号选择(显示用 displayOperands计算用 operandsVal
boolean addedBracket = false;
int startPos = findBracketStart(operandsVal, selectedOperators, 0.3);
StringBuilder expBuilder = new StringBuilder();
for (int i = 0; i < operandCount; i++) {
if (startPos != -1 && i == startPos) expBuilder.append("(");
expBuilder.append(displayOperands[i]);
if (startPos != -1 && i == startPos + 1) expBuilder.append(")");
if (i < operandCount - 1) expBuilder.append(" ").append(selectedOperators[i]).append(" ");
}
String expression = expBuilder.toString();
// 计算结果double
double result;
if (startPos != -1) {
double bracketResult = applyOperatorDouble(operandsVal[startPos], operandsVal[startPos + 1], selectedOperators[startPos]);
int newCount = operandCount - 1;
double[] newVals = new double[newCount];
String[] newOps = new String[newCount - 1];
for (int i = 0; i < startPos; i++) newVals[i] = operandsVal[i];
newVals[startPos] = bracketResult;
for (int i = startPos + 1; i < newCount; i++) newVals[i] = operandsVal[i + 1];
for (int i = 0; i < newCount - 1; i++) newOps[i] = (i < startPos) ? selectedOperators[i] : selectedOperators[i + 1];
result = newVals[0];
for (int i = 0; i < newCount - 1; i++) result = applyOperatorDouble(result, newVals[i + 1], newOps[i]);
} else {
result = operandsVal[0];
for (int i = 0; i < operandCount - 1; i++) result = applyOperatorDouble(result, operandsVal[i + 1], selectedOperators[i]);
}
return new MathProblem(expression, df.format(result), "高中");
}
}

@ -0,0 +1,174 @@
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
/**
*
*/
public class MathPaperGenerator {
private UserManager userManager;
private MathProblemGenerator problemGenerator;
private FileManager fileManager;
private Scanner scanner;
public MathPaperGenerator() {
this.userManager = new UserManager();
this.problemGenerator = new MathProblemGenerator();
this.fileManager = new FileManager();
this.scanner = new Scanner(System.in);
}
/**
*
*/
public static void main(String[] args) {
MathPaperGenerator generator = new MathPaperGenerator();
generator.run();
}
/**
*
*/
public void run() {
System.out.println("欢迎使用中小学数学卷子自动生成程序!");
System.out.println("=" + "=".repeat(40));
// 使用说明(菜单提示)
System.out.println("使用说明:");
System.out.println(" - 输入题目数量10-30生成试卷");
System.out.println(" - 输入 切换为 小学/初中/高中 来切换难度,例如:切换为 初中");
System.out.println(" - 输入 -1 退出当前用户,重新登录");
System.out.println();
while (true) {
User currentUser = login();
if (currentUser != null) {
handleUserSession(currentUser);
}
}
}
/*用户登录*/
private User login() {
while (true) {
System.out.print("请输入用户名和密码(用空格隔开): ");
String input = scanner.nextLine().trim();
if (input.isEmpty()) {
System.out.println("输入不能为空,请重新输入。");
continue;
}
String[] parts = input.split("\\s+");
String username = parts[0];
String password = parts[1];
User user = userManager.authenticate(username, password);
if (user != null) {
System.out.println("登录成功!当前选择为 " + user.getUserType() + " 出题");
// 登录后提示如何切换难度
System.out.println("提示:您可以在数量输入处键入 切换为 小学/初中/高中 来切换难度,例如:切换为 初中");
return user;
} else {
System.out.println("请输入正确的用户名、密码");
}
}
}
/**
*
* @param user
*/
private void handleUserSession(User user) {
String currentDifficulty = user.getUserType();
while (true) {
// 原提示替换为更详细的说明
System.out.println("准备生成 " + currentDifficulty + " 数学题目请输入题目数量10-30。");
System.out.println("提示:输入 切换为 小学/初中/高中 可切换难度(例如:切换为 初中);输入 -1 将退出当前用户并重新登录。");
String input = scanner.nextLine().trim();
// 检查是否要切换类型
if (input.startsWith("切换为")) {
String newType = input.substring(3).trim();
if (userManager.isValidUserType(newType)) {
currentDifficulty = newType;
System.out.println("已切换为 " + newType + " 出题模式");
continue;
} else {
System.out.println("请输入小学、初中和高中三个选项中的一个");
continue;
}
}
try {
int count = Integer.parseInt(input);
if (count == -1) {
System.out.println("退出当前用户,重新登录...");
System.out.println();
return;
}
if (count < 10 || count > 30) {
System.out.println("题目数量必须在10-30之间请重新输入。");
continue;
}
// 生成题目
generateAndSavePaper(user, currentDifficulty, count);
} catch (NumberFormatException e) {
System.out.println("请输入有效的数字。");
}
}
}
/**
*
* @param user
* @param difficulty
* @param count
*/
private void generateAndSavePaper(User user, String difficulty, int count) {
try {
System.out.println("正在生成 " + count + " 道 " + difficulty + " 数学题目...");
// 生成不重复的题目
List<MathProblem> problems = fileManager.generateUniqueProblems(
user, problemGenerator, count, difficulty);
if (problems.size() < count) {
System.out.println("注意:由于重复限制,实际生成了 " + problems.size() + " 道题目。");
}
// 保存到文件
String filePath = fileManager.savePaperToFile(user, problems, difficulty);
System.out.println("题目生成完成!");
System.out.println("文件已保存至: " + filePath);
System.out.println("题目预览:");
System.out.println("-" + "-".repeat(30));
// 显示前5道题目作为预览
int previewCount = Math.min(5, problems.size());
for (int i = 0; i < previewCount; i++) {
System.out.println((i + 1) + ". " + problems.get(i).toString());
}
if (problems.size() > 5) {
System.out.println("... 还有 " + (problems.size() - 5) + " 道题目");
}
System.out.println("-" + "-".repeat(30));
System.out.println();
// 生成完成后的操作提示
System.out.println("提示:如需切换难度,请在下一次输入中键入 切换为 小学/初中/高中,例如:切换为 高中。");
System.out.println("直接输入新的题目数量10-30将继续使用当前难度输入 -1 退出当前用户。");
} catch (IOException e) {
System.err.println("保存文件时出错: " + e.getMessage());
} catch (Exception e) {
System.err.println("生成题目时出错: " + e.getMessage());
}
}
}

@ -0,0 +1,44 @@
/**
*
*/
public class MathProblem {
private String expression; // 题目表达式
private String answer; // 答案
private String difficulty; // 难度级别
public MathProblem(String expression, String answer, String difficulty) {
this.expression = expression;
this.answer = answer;
this.difficulty = difficulty;
}
public String getExpression() {
return expression;
}
public String getAnswer() {
return answer;
}
public String getDifficulty() {
return difficulty;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MathProblem that = (MathProblem) obj;
return expression.equals(that.expression);
}
@Override
public int hashCode() {
return expression.hashCode();
}
@Override
public String toString() {
return expression + " = ?";
}
}

@ -0,0 +1,34 @@
import java.text.DecimalFormat;
import java.util.Random;
/**
*
* API
*/
public class MathProblemGenerator {
private final Random random;
private final DecimalFormat df;
public MathProblemGenerator() {
this.random = new Random();
this.df = new DecimalFormat("#.##");
}
/**
*
* @param difficulty
* @return
*/
public MathProblem generateProblem(String difficulty) {
switch (difficulty) {
case "小学":
return new ElementaryProblemGenerator(random, df).generate();
case "初中":
return new MiddleSchoolProblemGenerator(random, df).generate();
case "高中":
return new HighSchoolProblemGenerator(random, df).generate();
default:
throw new IllegalArgumentException("不支持的难度级别: " + difficulty);
}
}
}

@ -0,0 +1,69 @@
import java.text.DecimalFormat;
import java.util.Random;
public class MiddleSchoolProblemGenerator extends AbstractProblemGenerator {
public MiddleSchoolProblemGenerator(Random random, DecimalFormat df) {
super(random, df);
}
@Override
public MathProblem generate() {
int operandCount = random.nextInt(3) + 3; // 3-5 个操作数
int[] operands = new int[operandCount];
String[] selectedOperators = new String[operandCount - 1];
String[] displayOperands = new String[operandCount];
String[] operatorPool = {"+", "-", "*", "/"};
for (int i = 0; i < operandCount; i++) {
operands[i] = random.nextInt(100) + 1;
displayOperands[i] = String.valueOf(operands[i]);
}
for (int i = 0; i < operandCount - 1; i++) {
selectedOperators[i] = operatorPool[random.nextInt(operatorPool.length)];
}
// 平方或根号
int specialOperandIndex = random.nextInt(operandCount);
boolean useSquareRoot = random.nextBoolean();
if (useSquareRoot) {
int base = random.nextInt(24) + 2; //2-25
int perfectSquare = base * base;
operands[specialOperandIndex] = base; // √(base^2) = base
displayOperands[specialOperandIndex] = "√" + perfectSquare;
} else {
int base = random.nextInt(9) + 2; // 2-10
int square = base * base;
operands[specialOperandIndex] = square;
displayOperands[specialOperandIndex] = base + "²";
}
// 括号选择
boolean addedBracket = false;
int bracketStartIndex = findBracketStart(toDoubleArray(operands), selectedOperators, 0.3);
String expression;
double result;
if (bracketStartIndex != -1) {
// 构建带括号(显示用 displayOperands
StringBuilder expressionBuilder = new StringBuilder();
for (int i = 0; i < operandCount; i++) {
if (i == bracketStartIndex) expressionBuilder.append("(");
expressionBuilder.append(displayOperands[i]);
if (i == bracketStartIndex + 1) expressionBuilder.append(")");
if (i < operandCount - 1) expressionBuilder.append(" ").append(selectedOperators[i]).append(" ");
}
expression = expressionBuilder.toString();
result = evaluateExpressionWithBrackets(operands, selectedOperators, bracketStartIndex);
} else {
StringBuilder expressionBuilder = new StringBuilder();
expressionBuilder.append(displayOperands[0]);
result = operands[0];
for (int i = 0; i < operandCount - 1; i++) {
expressionBuilder.append(" ").append(selectedOperators[i]).append(" ").append(displayOperands[i + 1]);
result = applyOperator(result, operands[i + 1], selectedOperators[i]);
}
expression = expressionBuilder.toString();
}
return new MathProblem(expression, df.format(result), "初中");
}
}

@ -0,0 +1,42 @@
/**
*
*/
public class User {
private String username;
private String password;
private String userType; // 小学、初中、高中
public User(String username, String password, String userType) {
this.username = username;
this.password = password;
this.userType = userType;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getUserType() {
return userType;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return username.equals(user.username) && password.equals(user.password);
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", userType='" + userType + '\'' +
'}';
}
}

@ -0,0 +1,58 @@
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public class UserManager {
private Map<String, User> users;
public UserManager() {
initializeUsers();
}
/**
*
*/
private void initializeUsers() {
users = new HashMap<>();
// 小学账户
users.put("张三1", new User("张三1", "123", "小学"));
users.put("张三2", new User("张三2", "123", "小学"));
users.put("张三3", new User("张三3", "123", "小学"));
// 初中账户
users.put("李四1", new User("李四1", "123", "初中"));
users.put("李四2", new User("李四2", "123", "初中"));
users.put("李四3", new User("李四3", "123", "初中"));
// 高中账户
users.put("王五1", new User("王五1", "123", "高中"));
users.put("王五2", new User("王五2", "123", "高中"));
users.put("王五3", new User("王五3", "123", "高中"));
}
/**
*
* @param username
* @param password
* @return null
*/
public User authenticate(String username, String password) {
User user = users.get(username);
if (user != null && user.getPassword().equals(password)) {
return user;
}
return null;
}
/**
*
* @param userType
* @return
*/
public boolean isValidUserType(String userType) {
return "小学".equals(userType) || "初中".equals(userType) || "高中".equals(userType);
}
}
Loading…
Cancel
Save