commit #9

Merged
plks47r9b merged 1 commits from develop into main 4 months ago

Binary file not shown.

@ -27,6 +27,7 @@
├── Assign.java # 调度核心逻辑,负责用户登录和功能调用 ├── Assign.java # 调度核心逻辑,负责用户登录和功能调用
├── Function.java # 用户交互与出题数量输入处理 ├── Function.java # 用户交互与出题数量输入处理
├── Create.java # 调用生成器并保存试卷 ├── Create.java # 调用生成器并保存试卷
├── QuestionGenerator.java # Generator的父类
├── Generator.java # 随机数学题生成器(小学/初中/高中) ├── Generator.java # 随机数学题生成器(小学/初中/高中)
├── LoadFile.java # 读取用户已有题目,避免重复 ├── LoadFile.java # 读取用户已有题目,避免重复
├── Save.java # 保存生成的试卷到本地 ├── Save.java # 保存生成的试卷到本地

@ -4,7 +4,7 @@ public class Create {
public static void create(int n,Login.Level currentLevel,Login.Account user) { public static void create(int n,Login.Level currentLevel,Login.Account user) {
List<String> existing = LoadFile.loadExistingQuestions(user.username); List<String> existing = LoadFile.loadExistingQuestions(user.username);
Generator.QuestionGenerator qg = new Generator.QuestionGenerator(currentLevel, existing); Generator qg = new Generator(currentLevel, existing);
List<String> paper = qg.generatePaper(n); List<String> paper = qg.generatePaper(n);
if (paper.isEmpty()) { if (paper.isEmpty()) {
System.out.println("未能生成题目(可能因去重约束导致)。"); System.out.println("未能生成题目(可能因去重约束导致)。");

@ -1,196 +1,190 @@
import java.util.Random;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* *
*/ */
public class Generator { public class Generator extends QuestionGenerator {
private static final Random RAND = new Random();
private static final int MAX_ATTEMPTS = 2000;
/**
*
*/
static class QuestionGenerator {
private final Login.Level level; public static final Random RAND = new Random();
private final Set<String> existing; public static final int MAX_ATTEMPTS = 2000;
QuestionGenerator(Login.Level level, List<String> existingQuestions) { public final Login.Level level;
this.level = level; public final Set<String> existing;
this.existing = existingQuestions.stream().map(String::trim).collect(Collectors.toSet());
}
/** Generator(Login.Level level, List<String> existingQuestions) {
* this.level = level;
* this.existing = existingQuestions.stream().map(String::trim).collect(Collectors.toSet());
* @param n }
* @return
*/
List<String> generatePaper(int n) {
Set<String> generated = new LinkedHashSet<>();
// 防止死循环
int attempts = 0;
while (generated.size() < n && attempts < MAX_ATTEMPTS) {
attempts++;
String q = generateOneQuestion();
// 统一去掉题号与多余空白来比对
String key = normalize(q);
//与文件内+本次前面生成的查重
if (!existing.contains(key) && !generated.contains(key)) {
generated.add(q);
}
}
if (generated.size() < n) { /**
System.out.println( *
"注意:无法生成足够的不重复题目,已生成 " *
+ generated.size() + " 道题(请求 " + n + " 道)"); * @param n
* @return
*/
public List<String> generatePaper(int n) {
Set<String> generated = new LinkedHashSet<>();
// 防止死循环
int attempts = 0;
while (generated.size() < n && attempts < MAX_ATTEMPTS) {
attempts++;
String q = generateOneQuestion();
// 统一去掉题号与多余空白来比对
String key = normalize(q);
//与文件内+本次前面生成的查重
if (!existing.contains(key) && !generated.contains(key)) {
generated.add(q);
} }
return new ArrayList<>(generated);
} }
//去空格 if (generated.size() < n) {
private String normalize(String s) { System.out.println(
return s.replaceAll("\\s+", "").toLowerCase(); "注意:无法生成足够的不重复题目,已生成 "
+ generated.size() + " 道题(请求 " + n + " 道)");
} }
return new ArrayList<>(generated);
}
// 生成单题主逻辑 //去空格
private String generateOneQuestion() { public String normalize(String s) {
int operands = RAND.nextInt(5) + 1; // 保证 1..5 个操作数 return s.replaceAll("\\s+", "").toLowerCase();
int operands_p = RAND.nextInt(4) + 2; // 保证 2..5 个操作数 }
return switch (level) {
case PRIMARY -> genPrimary(operands_p); // 生成单题主逻辑
case MIDDLE -> genMiddle(operands); public String generateOneQuestion() {
case HIGH -> genHigh(operands); int operands = RAND.nextInt(5) + 1; // 保证 1..5 个操作数
}; int operands_p = RAND.nextInt(4) + 2; // 保证 2..5 个操作数
} return switch (level) {
case PRIMARY -> genPrimary(operands_p);
case MIDDLE -> genMiddle(operands);
case HIGH -> genHigh(operands);
};
}
// 生成小学题(只有 + - * / 和括号) // 生成小学题(只有 + - * / 和括号)
private String genPrimary(int operands) { public String genPrimary(int operands) {
if (operands == 1) { if (operands == 1) {
return String.valueOf(randInt(1, 100)); return String.valueOf(randInt(1, 100));
}
List<String> ops = Arrays.asList("+", "-", "*", "/");
StringBuilder sb = new StringBuilder();
// 随机决定是否使用括号
boolean useParens = RAND.nextBoolean();
if (useParens && operands >= 3 && RAND.nextBoolean()) {
// 构造 (a op b) op c ...
sb.append("(");
sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ")
.append(randInt(1, 100));
sb.append(")");
for (int i = 2; i < operands; i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
} }
List<String> ops = Arrays.asList("+", "-", "*", "/"); } else {
StringBuilder sb = new StringBuilder(); // 直连
// 随机决定是否使用括号 sb.append(randInt(1, 100));
boolean useParens = RAND.nextBoolean(); for (int i = 1; i < operands; i++) {
if (useParens && operands >= 3 && RAND.nextBoolean()) { sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
// 构造 (a op b) op c ...
sb.append("(");
sb.append(randInt(1, 100)).append(" ").append(randomChoice(ops)).append(" ")
.append(randInt(1, 100));
sb.append(")");
for (int i = 2; i < operands; i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
}
} else {
// 直连
sb.append(randInt(1, 100));
for (int i = 1; i < operands; i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1, 100));
}
} }
return sb.toString();
} }
return sb.toString();
}
// 生成初中题:至少包含一个 ^2 或 sqrt() // 生成初中题:至少包含一个 ^2 或 sqrt()
private String genMiddle(int operands) { public String genMiddle(int operands) {
// 基本表达式生成,后插入平方或开根号 // 基本表达式生成,后插入平方或开根号
String expr = genPrimary(operands); // 基本算术 String expr = genPrimary(operands); // 基本算术
// 2选1 // 2选1
if (RAND.nextBoolean()) { if (RAND.nextBoolean()) {
// 找一个数字位置并替换为 (x)^2 // 找一个数字位置并替换为 (x)^2
expr = applySquare(expr); expr = applySquare(expr);
} else { } else {
expr = applySqrt(expr); expr = applySqrt(expr);
}
return expr;
} }
return expr;
}
// 生成高中题:至少包含 sin/cos/tan // 生成高中题:至少包含 sin/cos/tan
private String genHigh(int operands) { public String genHigh(int operands) {
String expr = genPrimary(operands); String expr = genPrimary(operands);
expr = applyTrig(expr); expr = applyTrig(expr);
return expr; return expr;
} }
// 把表达式中某个数字替换为 (x)^2 // 把表达式中某个数字替换为 (x)^2
private String applySquare(String expr) { public String applySquare(String expr) {
// 寻找所有数字的片段 // 寻找所有数字的片段
List<int[]> spans = findNumberSpans(expr); List<int[]> spans = findNumberSpans(expr);
if (spans.isEmpty()) { if (spans.isEmpty()) {
return expr + "^2"; return expr + "^2";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + "(" + num + ")^2" + after;
} }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + "(" + num + ")^2" + after;
}
// 把表达式中某个数字替换为 sqrt(x) // 把表达式中某个数字替换为 sqrt(x)
private String applySqrt(String expr) { public String applySqrt(String expr) {
List<int[]> spans = findNumberSpans(expr); List<int[]> spans = findNumberSpans(expr);
if (spans.isEmpty()) { if (spans.isEmpty()) {
return "sqrt(" + expr + ")"; return "sqrt(" + expr + ")";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + "sqrt(" + num + ")" + after;
} }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + "sqrt(" + num + ")" + after;
}
// 把某个数字或子表达式替换为 sin(x)/cos(x)/tan(x) // 把某个数字或子表达式替换为 sin(x)/cos(x)/tan(x)
private String applyTrig(String expr) { public String applyTrig(String expr) {
List<int[]> spans = findNumberSpans(expr); List<int[]> spans = findNumberSpans(expr);
String func = randomChoice(Arrays.asList("sin", "cos", "tan")); String func = randomChoice(Arrays.asList("sin", "cos", "tan"));
if (spans.isEmpty()) { if (spans.isEmpty()) {
return func + "(" + expr + ")"; return func + "(" + expr + ")";
}
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + func + "(" + num + ")" + after;
} }
int[] s = spans.get(RAND.nextInt(spans.size()));
String before = expr.substring(0, s[0]);
String num = expr.substring(s[0], s[1]);
String after = expr.substring(s[1]);
return before + func + "(" + num + ")" + after;
}
// 找出数字位置 // 找出数字位置
private List<int[]> findNumberSpans(String expr) { public List<int[]> findNumberSpans(String expr) {
List<int[]> spans = new ArrayList<>(); List<int[]> spans = new ArrayList<>();
char[] chs = expr.toCharArray(); char[] chs = expr.toCharArray();
int i = 0, n = chs.length; int i = 0, n = chs.length;
while (i < n) { while (i < n) {
if (Character.isDigit(chs[i])) { if (Character.isDigit(chs[i])) {
int j = i; int j = i;
while (j < n && (Character.isDigit(chs[j]))) { while (j < n && (Character.isDigit(chs[j]))) {
j++; j++;
}
spans.add(new int[]{i, j});
i = j;
} else {
i++;
} }
spans.add(new int[]{i, j});
i = j;
} else {
i++;
} }
return spans;
} }
return spans;
}
//a..b随机数 //a..b随机数
private int randInt(int a, int b) { public int randInt(int a, int b) {
return RAND.nextInt(b - a + 1) + a; return RAND.nextInt(b - a + 1) + a;
} }
//抽签功能 //抽签功能
private <T> T randomChoice(List<T> list) { public <T> T randomChoice(List<T> list) {
return list.get(RAND.nextInt(list.size())); return list.get(RAND.nextInt(list.size()));
}
} }
} }

@ -0,0 +1,46 @@
import java.util.List;
public abstract class QuestionGenerator {
protected QuestionGenerator() {
}
public abstract List<String> generatePaper(int n);
//去空格
public abstract String normalize(String s);
// 生成单题主逻辑
public abstract String generateOneQuestion();
// 生成小学题(只有 + - * / 和括号)
public abstract String genPrimary(int operands);
// 生成初中题:至少包含一个 ^2 或 sqrt()
public abstract String genMiddle(int operands);
// 生成高中题:至少包含 sin/cos/tan
public abstract String genHigh(int operands);
// 把表达式中某个数字替换为 (x)^2
public abstract String applySquare(String expr);
// 把表达式中某个数字替换为 sqrt(x)
public abstract String applySqrt(String expr);
// 把某个数字或子表达式替换为 sin(x)/cos(x)/tan(x)
public abstract String applyTrig(String expr);
// 找出数字位置
public abstract List<int[]> findNumberSpans(String expr);
//a..b随机数
public abstract int randInt(int a, int b);
//抽签功能
public abstract <T> T randomChoice(List<T> list);
}
Loading…
Cancel
Save