|
|
package model;
|
|
|
|
|
|
import java.util.List;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Set;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Random;
|
|
|
import java.text.DecimalFormat;
|
|
|
|
|
|
public class QuestionGenerator {
|
|
|
private Random random = new Random();
|
|
|
private DecimalFormat df = new DecimalFormat("#.##"); // 保留两位小数的格式化器
|
|
|
public List<Question> generateQuestions(int count, String difficulty) {
|
|
|
List<Question> questions = new ArrayList<>();
|
|
|
Set<String> existingQuestions = new HashSet<>();
|
|
|
|
|
|
while (questions.size() < count) {
|
|
|
Question question = generateQuestion(difficulty);
|
|
|
String questionKey = question.getQuestion() + Arrays.toString(question.getOptions());
|
|
|
|
|
|
if (!existingQuestions.contains(questionKey)) {
|
|
|
questions.add(question);
|
|
|
existingQuestions.add(questionKey);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return questions;
|
|
|
}
|
|
|
|
|
|
private Question generateQuestion(String difficulty) {
|
|
|
switch (difficulty) {
|
|
|
case "小学":
|
|
|
return generatePrimaryQuestion();
|
|
|
case "初中":
|
|
|
return generateMiddleSchoolQuestion();
|
|
|
case "高中":
|
|
|
return generateHighSchoolQuestion();
|
|
|
default:
|
|
|
return generatePrimaryQuestion();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private Question generatePrimaryQuestion() {
|
|
|
int num1 = random.nextInt(50) + 1;
|
|
|
int num2 = random.nextInt(50) + 1;
|
|
|
int num3 = random.nextInt(50) + 1;
|
|
|
char[] operators = {'+', '-', '*', '/'};
|
|
|
char operator1 = operators[random.nextInt(operators.length)];
|
|
|
char operator2 = operators[random.nextInt(operators.length)];
|
|
|
String question;
|
|
|
double correctAnswer;
|
|
|
// 随机决定是否使用括号
|
|
|
if (random.nextBoolean()) {
|
|
|
// 有括号的情况
|
|
|
switch (random.nextInt(3)) {
|
|
|
case 0: // (a op b) op c
|
|
|
correctAnswer = calculate(calculate(num1, num2, operator1), num3, operator2);
|
|
|
question = "(" + num1 + " " + getOperatorSymbol(operator1) + " " + num2 + ") " +
|
|
|
getOperatorSymbol(operator2) + " " + num3 + " = ?";
|
|
|
break;
|
|
|
case 1: // a op (b op c)
|
|
|
correctAnswer = calculate(num1, calculate(num2, num3, operator2), operator1);
|
|
|
question = num1 + " " + getOperatorSymbol(operator1) + " (" + num2 + " " +
|
|
|
getOperatorSymbol(operator2) + " " + num3 + ") = ?";
|
|
|
break;
|
|
|
default: // 无括号
|
|
|
correctAnswer = calculate(calculate(num1, num2, operator1), num3, operator2);
|
|
|
question = num1 + " " + getOperatorSymbol(operator1) + " " + num2 + " " +
|
|
|
getOperatorSymbol(operator2) + " " + num3 + " = ?";
|
|
|
break;
|
|
|
}
|
|
|
} else {
|
|
|
if (operator1 == '/') {
|
|
|
num2 = random.nextInt(10) + 1; // 避免除零
|
|
|
}
|
|
|
correctAnswer = calculate(num1, num2, operator1);
|
|
|
question = num1 + " " + getOperatorSymbol(operator1) + " " + num2 + " = ?";
|
|
|
}
|
|
|
correctAnswer = Double.parseDouble(df.format(correctAnswer));
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
return new Question(question, options, getCorrectOptionIndex(options, correctAnswer), "小学");
|
|
|
}
|
|
|
|
|
|
private Question generateMiddleSchoolQuestion() {
|
|
|
String question;
|
|
|
double correctAnswer;
|
|
|
|
|
|
if (random.nextBoolean()) {
|
|
|
// 平方或平方根题目
|
|
|
int num = random.nextInt(20) + 1;
|
|
|
if (random.nextBoolean()) {
|
|
|
correctAnswer = Math.pow(num, 2);
|
|
|
question = num + "² = ?";
|
|
|
} else {
|
|
|
int squared = num * num;
|
|
|
correctAnswer = num;
|
|
|
question = "√" + squared + " = ?";
|
|
|
}
|
|
|
} else {
|
|
|
// 带加减乘除的题目
|
|
|
int num1 = random.nextInt(50) + 1;
|
|
|
int num2 = random.nextInt(50) + 1;
|
|
|
int num3 = random.nextInt(20) + 1;
|
|
|
char[] operators = {'+', '-', '*', '/'};
|
|
|
char operator1 = operators[random.nextInt(operators.length)];
|
|
|
char operator2 = operators[random.nextInt(operators.length)];
|
|
|
|
|
|
// 随机组合平方、平方根和基本运算
|
|
|
if (random.nextBoolean()) {
|
|
|
// 平方与基本运算组合
|
|
|
correctAnswer = calculate(Math.pow(num1, 2), num2, operator1);
|
|
|
question = num1 + "² " + getOperatorSymbol(operator1) + " " + num2 + " = ?";
|
|
|
} else {
|
|
|
// 平方根与基本运算组合
|
|
|
int squared = num3 * num3;
|
|
|
correctAnswer = calculate(num3, num2, operator1);
|
|
|
question = "√" + squared + " " + getOperatorSymbol(operator1) + " " + num2 + " = ?";
|
|
|
}
|
|
|
}
|
|
|
correctAnswer = Double.parseDouble(df.format(correctAnswer));
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
return new Question(question, options, getCorrectOptionIndex(options, correctAnswer), "初中");
|
|
|
}
|
|
|
|
|
|
private Question generateHighSchoolQuestion() {
|
|
|
|
|
|
if (random.nextBoolean()) {
|
|
|
return HighSchoolQuestionOne();
|
|
|
} else {
|
|
|
// 三角函数与基本运算组合
|
|
|
return HighSchoolQuestionTwo();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
private Question HighSchoolQuestionOne() {
|
|
|
String question;
|
|
|
double correctAnswer;
|
|
|
double angle = random.nextInt(360);
|
|
|
String[] trigFunctions = {"sin", "cos", "tan"};
|
|
|
String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)];
|
|
|
|
|
|
switch (trigFunction) {
|
|
|
case "sin":
|
|
|
correctAnswer = Math.sin(Math.toRadians(angle));
|
|
|
question = "sin(" + angle + "°) = ?";
|
|
|
break;
|
|
|
case "cos":
|
|
|
correctAnswer = Math.cos(Math.toRadians(angle));
|
|
|
question = "cos(" + angle + "°) = ?";
|
|
|
break;
|
|
|
case "tan":
|
|
|
angle = getSafeTanAngle();
|
|
|
correctAnswer = Math.tan(Math.toRadians(angle));
|
|
|
question = "tan(" + angle + "°) = ?";
|
|
|
break;
|
|
|
default:
|
|
|
correctAnswer = 0;
|
|
|
question = "";
|
|
|
}
|
|
|
correctAnswer = Double.parseDouble(df.format(correctAnswer));
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
return new Question(question, options, getCorrectOptionIndex(options, correctAnswer), "高中");
|
|
|
}
|
|
|
private Question HighSchoolQuestionTwo() {
|
|
|
String question;
|
|
|
double correctAnswer;
|
|
|
double angle = random.nextInt(360);
|
|
|
int num = random.nextInt(20) + 1;
|
|
|
char[] operators = {'+', '-', '*', '/'};
|
|
|
char operator = operators[random.nextInt(operators.length)];
|
|
|
String[] trigFunctions = {"sin", "cos", "tan"};
|
|
|
String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)];
|
|
|
|
|
|
double trigValue;
|
|
|
switch (trigFunction) {
|
|
|
case "sin":
|
|
|
trigValue = Math.sin(Math.toRadians(angle));
|
|
|
question = "sin(" + angle + "°) " + getOperatorSymbol(operator) + " " + num + " = ?";
|
|
|
break;
|
|
|
case "cos":
|
|
|
trigValue = Math.cos(Math.toRadians(angle));
|
|
|
question = "cos(" + angle + "°) " + getOperatorSymbol(operator) + " " + num + " = ?";
|
|
|
break;
|
|
|
case "tan":
|
|
|
angle = getSafeTanAngle();
|
|
|
trigValue = Math.tan(Math.toRadians(angle));
|
|
|
question = "tan(" + angle + "°) " + getOperatorSymbol(operator) + " " + num + " = ?";
|
|
|
break;
|
|
|
default:
|
|
|
trigValue = 0;
|
|
|
question = "";
|
|
|
}
|
|
|
trigValue = Double.parseDouble(df.format(trigValue));
|
|
|
correctAnswer = calculate(trigValue, num, operator);
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
return new Question(question, options, getCorrectOptionIndex(options, correctAnswer), "高中");
|
|
|
}
|
|
|
// 获取安全的tan角度(更严格的范围)
|
|
|
private double getSafeTanAngle() {
|
|
|
// 为tan函数提供更安全的角度范围,避免无限大的值
|
|
|
// 排除:85°-95°, 265°-275° 等接近90°和270°的角度
|
|
|
List<Double> safeTanAngles = new ArrayList<>();
|
|
|
for (int i = 0; i < 360; i++) {
|
|
|
if (isSafeForTan(i)) {
|
|
|
safeTanAngles.add((double) i);
|
|
|
}
|
|
|
}
|
|
|
return safeTanAngles.get(random.nextInt(safeTanAngles.size()));
|
|
|
}
|
|
|
// 检查角度是否对tan函数安全(更严格的条件)
|
|
|
private boolean isSafeForTan(double angle) {
|
|
|
// 排除接近90°、270°的角度,以及排除tan值过大的角度
|
|
|
return !((angle >= 80 && angle <= 100) ||
|
|
|
(angle >= 260 && angle <= 280) ||
|
|
|
(angle >= 170 && angle <= 190) ||
|
|
|
(angle >= 350 || angle <= 10));
|
|
|
}
|
|
|
|
|
|
// 计算方法
|
|
|
private double calculate(double a, double b, char operator) {
|
|
|
switch (operator) {
|
|
|
case '+':
|
|
|
return a + b;
|
|
|
case '-':
|
|
|
return a - b;
|
|
|
case '*':
|
|
|
return a * b;
|
|
|
case '/':
|
|
|
return b != 0 ? a / b : a; // 避免除零
|
|
|
default:
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取运算符符号
|
|
|
private String getOperatorSymbol(char operator) {
|
|
|
switch (operator) {
|
|
|
case '+':
|
|
|
return "+";
|
|
|
case '-':
|
|
|
return "-";
|
|
|
case '*':
|
|
|
return "×";
|
|
|
case '/':
|
|
|
return "÷";
|
|
|
default:
|
|
|
return "";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private String[] generateOptions(double correctAnswer) {
|
|
|
String[] options = new String[4];
|
|
|
int correctIndex = random.nextInt(4);
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
if (i == correctIndex) {
|
|
|
options[i] = df.format(correctAnswer);
|
|
|
} else {
|
|
|
double wrongAnswer;
|
|
|
do {
|
|
|
double variation = (random.nextDouble() - 0.5) * 10;
|
|
|
wrongAnswer = correctAnswer + variation;
|
|
|
wrongAnswer = Double.parseDouble(df.format(wrongAnswer));
|
|
|
} while (Math.abs(wrongAnswer - correctAnswer) < 0.1);
|
|
|
options[i] = String.valueOf(wrongAnswer);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return options;
|
|
|
}
|
|
|
|
|
|
private int getCorrectOptionIndex(String[] options, double correctAnswer) {
|
|
|
for (int i = 0; i < options.length; i++) {
|
|
|
if (Double.parseDouble(options[i]) == correctAnswer) {
|
|
|
return i;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
} |