|
|
|
|
@ -14,7 +14,7 @@ from typing import List, Set, Optional
|
|
|
|
|
|
|
|
|
|
class Expression:
|
|
|
|
|
"""
|
|
|
|
|
数学表达式类
|
|
|
|
|
数学表达式类(辅助类)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, expression_str: str, answer: float):
|
|
|
|
|
@ -97,42 +97,16 @@ class ElementaryQuestionGenerator(QuestionGenerator):
|
|
|
|
|
"""
|
|
|
|
|
max_attempts = 100
|
|
|
|
|
for _ in range(max_attempts):
|
|
|
|
|
# 随机生成操作数数量(2-5个)
|
|
|
|
|
num_operands = random.randint(2, 5)
|
|
|
|
|
operands = [random.randint(1, 50) for _ in range(num_operands)]
|
|
|
|
|
operators = [random.choice(['+', '-', '*']) if random.random() < 0.8 else '/'
|
|
|
|
|
for _ in range(num_operands - 1)] # 减少减法和除法概率以避免负数
|
|
|
|
|
|
|
|
|
|
# 随机添加括号
|
|
|
|
|
expression_parts = []
|
|
|
|
|
for i in range(num_operands):
|
|
|
|
|
expression_parts.append(str(operands[i]))
|
|
|
|
|
if i < len(operators):
|
|
|
|
|
expression_parts.append(operators[i])
|
|
|
|
|
|
|
|
|
|
# 随机添加括号
|
|
|
|
|
if num_operands >= 3 and random.random() < 0.3:
|
|
|
|
|
# 在随机位置添加括号
|
|
|
|
|
open_pos = random.randint(0, len(expression_parts) - 3)
|
|
|
|
|
# 确保括号内至少有两个操作数
|
|
|
|
|
close_pos = min(open_pos + 2 + random.randint(1, 2) * 2, len(expression_parts))
|
|
|
|
|
|
|
|
|
|
# 确保括号位置是操作数位置
|
|
|
|
|
if open_pos % 2 == 0 and close_pos % 2 == 0:
|
|
|
|
|
expression_parts.insert(open_pos, '(')
|
|
|
|
|
expression_parts.insert(close_pos + 1, ')')
|
|
|
|
|
|
|
|
|
|
expression_str = ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
expression_str = self._generate_elementary_expression()
|
|
|
|
|
# 验证表达式是否有效
|
|
|
|
|
try:
|
|
|
|
|
# 替换除法符号以便计算
|
|
|
|
|
eval_expr = expression_str.replace('/', '/').replace('*', '*')
|
|
|
|
|
result = eval(eval_expr)
|
|
|
|
|
|
|
|
|
|
# 确保结果是非负数且是合理的(整数或有限小数)
|
|
|
|
|
if isinstance(result, (int, float)) and result >= 0 and abs(result) < 1000:
|
|
|
|
|
# 格式化结果,保留合适的小数位数
|
|
|
|
|
if (isinstance(result, (int, float)) and
|
|
|
|
|
result >= 0 and
|
|
|
|
|
abs(result) < 1000):
|
|
|
|
|
if isinstance(result, float) and result.is_integer():
|
|
|
|
|
result = int(result)
|
|
|
|
|
|
|
|
|
|
@ -141,7 +115,8 @@ class ElementaryQuestionGenerator(QuestionGenerator):
|
|
|
|
|
if str(expr) not in self.generated_questions:
|
|
|
|
|
self.generated_questions.add(str(expr))
|
|
|
|
|
# 将表达式中的乘号和除号替换为更易读的形式
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×').replace('/', '÷')
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×')
|
|
|
|
|
readable_expr_str = readable_expr_str.replace('/', '÷')
|
|
|
|
|
readable_expr = Expression(readable_expr_str, result)
|
|
|
|
|
return readable_expr
|
|
|
|
|
except:
|
|
|
|
|
@ -153,6 +128,65 @@ class ElementaryQuestionGenerator(QuestionGenerator):
|
|
|
|
|
readable_expr = Expression("1+1", 2)
|
|
|
|
|
return readable_expr
|
|
|
|
|
|
|
|
|
|
def _generate_elementary_expression(self) -> str:
|
|
|
|
|
"""
|
|
|
|
|
生成小学题目表达式字符串
|
|
|
|
|
|
|
|
|
|
@return: 表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
# 随机生成操作数数量(2-5个)
|
|
|
|
|
num_operands = random.randint(2, 5)
|
|
|
|
|
# 修改操作数范围从1-50到1-100
|
|
|
|
|
operands = [random.randint(1, 100) for _ in range(num_operands)]
|
|
|
|
|
operators = [random.choice(['+', '-', '*']) if random.random() < 0.8 else '/'
|
|
|
|
|
for _ in range(num_operands - 1)] # 减少减法和除法概率以避免负数
|
|
|
|
|
|
|
|
|
|
# 构建表达式部分
|
|
|
|
|
expression_parts = self._build_expression_parts(operands, operators)
|
|
|
|
|
|
|
|
|
|
# 随机添加括号
|
|
|
|
|
expression_parts = self._add_parentheses(expression_parts, num_operands)
|
|
|
|
|
|
|
|
|
|
return ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
def _build_expression_parts(self, operands: List[int],
|
|
|
|
|
operators: List[str]) -> List[str]:
|
|
|
|
|
"""
|
|
|
|
|
构建表达式部分
|
|
|
|
|
|
|
|
|
|
@param operands: 操作数列表
|
|
|
|
|
@param operators: 操作符列表
|
|
|
|
|
@return: 表达式部分列表
|
|
|
|
|
"""
|
|
|
|
|
expression_parts = []
|
|
|
|
|
for i in range(len(operands)):
|
|
|
|
|
expression_parts.append(str(operands[i]))
|
|
|
|
|
if i < len(operators):
|
|
|
|
|
expression_parts.append(operators[i])
|
|
|
|
|
return expression_parts
|
|
|
|
|
|
|
|
|
|
def _add_parentheses(self, expression_parts: List[str],
|
|
|
|
|
num_operands: int) -> List[str]:
|
|
|
|
|
"""
|
|
|
|
|
随机添加括号到表达式
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@param num_operands: 操作数数量
|
|
|
|
|
@return: 添加括号后的表达式部分列表
|
|
|
|
|
"""
|
|
|
|
|
if num_operands >= 3 and random.random() < 0.3:
|
|
|
|
|
# 在随机位置添加括号
|
|
|
|
|
open_pos = random.randint(0, len(expression_parts) - 3)
|
|
|
|
|
# 确保括号内至少有两个操作数
|
|
|
|
|
close_pos = min(open_pos + 2 + random.randint(1, 2) * 2,
|
|
|
|
|
len(expression_parts))
|
|
|
|
|
|
|
|
|
|
# 确保括号位置是操作数位置
|
|
|
|
|
if open_pos % 2 == 0 and close_pos % 2 == 0:
|
|
|
|
|
expression_parts.insert(open_pos, '(')
|
|
|
|
|
expression_parts.insert(close_pos + 1, ')')
|
|
|
|
|
return expression_parts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MiddleQuestionGenerator(QuestionGenerator):
|
|
|
|
|
"""
|
|
|
|
|
@ -167,93 +201,161 @@ class MiddleQuestionGenerator(QuestionGenerator):
|
|
|
|
|
"""
|
|
|
|
|
max_attempts = 100
|
|
|
|
|
for _ in range(max_attempts):
|
|
|
|
|
expression_parts = []
|
|
|
|
|
|
|
|
|
|
# 确保至少有一个平方或开根号
|
|
|
|
|
has_square_or_sqrt = True # 确保至少有一个平方或开根号
|
|
|
|
|
|
|
|
|
|
# 生成操作数数量(2-5个)
|
|
|
|
|
num_operands = random.randint(2, 5)
|
|
|
|
|
|
|
|
|
|
# 标记是否已添加特殊操作
|
|
|
|
|
special_added = False
|
|
|
|
|
|
|
|
|
|
for i in range(num_operands):
|
|
|
|
|
# 确保至少添加一个平方或开根号
|
|
|
|
|
if not special_added and i == num_operands - 1:
|
|
|
|
|
# 如果还没添加特殊操作,强制在最后一个操作数添加
|
|
|
|
|
choice = random.choice([0, 1]) # 提高开根号概率
|
|
|
|
|
if choice == 0:
|
|
|
|
|
# 添加平方
|
|
|
|
|
base = random.randint(1, 12)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
else:
|
|
|
|
|
# 添加开根号
|
|
|
|
|
square = random.randint(1, 12)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
special_added = True
|
|
|
|
|
else:
|
|
|
|
|
rand_val = random.random()
|
|
|
|
|
# 修改这里的概率,增加开根号和平方的出现频率
|
|
|
|
|
if not special_added and rand_val < 0.6: # 提高特殊操作概率从0.4到0.6
|
|
|
|
|
# 添加特殊操作(平方或开根号)
|
|
|
|
|
choice = random.choice([0, 1]) if random.random() < 0.6 else 1 # 提高开根号概率
|
|
|
|
|
if choice == 0:
|
|
|
|
|
# 添加平方
|
|
|
|
|
base = random.randint(1, 12)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
else:
|
|
|
|
|
# 添加开根号
|
|
|
|
|
square = random.randint(1, 12)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
special_added = True
|
|
|
|
|
else:
|
|
|
|
|
# 普通操作数
|
|
|
|
|
expression_parts.append(str(random.randint(1, 50)))
|
|
|
|
|
|
|
|
|
|
# 添加运算符(除了最后一个操作数)
|
|
|
|
|
if i < num_operands - 1:
|
|
|
|
|
expression_parts.append(random.choice(['+', '-', '*', '/']))
|
|
|
|
|
|
|
|
|
|
expression_str = ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
# 处理可能的语法问题
|
|
|
|
|
expression_str = self._generate_middle_expression()
|
|
|
|
|
expression_str = self.fix_expression_syntax(expression_str)
|
|
|
|
|
|
|
|
|
|
# 计算结果
|
|
|
|
|
try:
|
|
|
|
|
# 替换表达式中的函数以便计算
|
|
|
|
|
eval_expr = expression_str.replace('²', '**2').replace('√', 'math.sqrt')
|
|
|
|
|
eval_expr = self._prepare_middle_expression_for_eval(expression_str)
|
|
|
|
|
result = eval(eval_expr)
|
|
|
|
|
|
|
|
|
|
# 确保结果是合理的
|
|
|
|
|
if isinstance(result, (int, float)) and abs(result) < 1000 and not math.isnan(result):
|
|
|
|
|
if (isinstance(result, (int, float)) and
|
|
|
|
|
abs(result) < 1000 and
|
|
|
|
|
not math.isnan(result)):
|
|
|
|
|
# 格式化结果
|
|
|
|
|
if isinstance(result, float) and abs(result - round(result)) < 1e-10:
|
|
|
|
|
result = int(round(result))
|
|
|
|
|
|
|
|
|
|
expr = Expression(expression_str, result)
|
|
|
|
|
# 检查是否已生成过相同题目
|
|
|
|
|
if str(expr) not in self.generated_questions:
|
|
|
|
|
self.generated_questions.add(str(expr))
|
|
|
|
|
# 将表达式中的乘号和除号替换为更易读的形式
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×').replace('/', '÷')
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×')
|
|
|
|
|
readable_expr_str = readable_expr_str.replace('/', '÷')
|
|
|
|
|
readable_expr = Expression(readable_expr_str, result)
|
|
|
|
|
return readable_expr
|
|
|
|
|
except:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 如果无法生成有效题目,返回默认题目
|
|
|
|
|
expr = Expression("√4+2²", 6)
|
|
|
|
|
self.generated_questions.add(str(expr))
|
|
|
|
|
readable_expr = Expression("√4+2²", 6)
|
|
|
|
|
# 将表达式中的乘号和除号替换为更易读的形式
|
|
|
|
|
readable_expr_str = "√4+2²".replace('*', '×').replace('/', '÷')
|
|
|
|
|
readable_expr = Expression(readable_expr_str, 6)
|
|
|
|
|
return readable_expr
|
|
|
|
|
|
|
|
|
|
def _generate_middle_expression(self) -> str:
|
|
|
|
|
"""
|
|
|
|
|
生成初中题目表达式字符串
|
|
|
|
|
|
|
|
|
|
@return: 表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
expression_parts = []
|
|
|
|
|
|
|
|
|
|
# 生成操作数数量(2-5个)
|
|
|
|
|
num_operands = random.randint(2, 5)
|
|
|
|
|
|
|
|
|
|
# 标记是否已添加特殊操作
|
|
|
|
|
special_added = False
|
|
|
|
|
|
|
|
|
|
# 生成表达式部分
|
|
|
|
|
expression_parts, special_added = self._build_middle_expression_parts(
|
|
|
|
|
expression_parts, num_operands, special_added)
|
|
|
|
|
|
|
|
|
|
return ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
def _build_middle_expression_parts(self, expression_parts: List[str],
|
|
|
|
|
num_operands: int,
|
|
|
|
|
special_added: bool) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
构建初中表达式部分
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@param num_operands: 操作数数量
|
|
|
|
|
@param special_added: 是否已添加特殊操作标记
|
|
|
|
|
@return: (表达式部分列表, 是否已添加特殊操作标记)
|
|
|
|
|
"""
|
|
|
|
|
for i in range(num_operands):
|
|
|
|
|
# 确保至少添加一个平方或开根号
|
|
|
|
|
if not special_added and i == num_operands - 1:
|
|
|
|
|
# 如果还没添加特殊操作,强制在最后一个操作数添加
|
|
|
|
|
expression_parts, special_added = self._add_forced_special_operation(
|
|
|
|
|
expression_parts)
|
|
|
|
|
else:
|
|
|
|
|
expression_parts, special_added = self._add_middle_operand_or_operation(
|
|
|
|
|
expression_parts, special_added, i, num_operands)
|
|
|
|
|
|
|
|
|
|
# 添加运算符(除了最后一个操作数)
|
|
|
|
|
if i < num_operands - 1:
|
|
|
|
|
expression_parts.append(random.choice(['+', '-', '*', '/']))
|
|
|
|
|
|
|
|
|
|
return expression_parts, special_added
|
|
|
|
|
|
|
|
|
|
def _add_forced_special_operation(self, expression_parts: List[str]) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
强制添加特殊操作(平方或开根号)
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@return: (表达式部分列表, 是否已添加特殊操作标记)
|
|
|
|
|
"""
|
|
|
|
|
choice = random.choice([0, 1]) # 0为平方,1为开根号
|
|
|
|
|
if choice == 0:
|
|
|
|
|
# 添加平方
|
|
|
|
|
base = random.randint(1, 100)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
else:
|
|
|
|
|
# 添加开根号(确保至少有一个开根号)
|
|
|
|
|
square = random.randint(1, 10)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
return expression_parts, True
|
|
|
|
|
|
|
|
|
|
def _add_middle_operand_or_operation(self, expression_parts: List[str],
|
|
|
|
|
special_added: bool, i: int,
|
|
|
|
|
num_operands: int) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
添加初中操作数或操作
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@param special_added: 是否已添加特殊操作标记
|
|
|
|
|
@param i: 当前索引
|
|
|
|
|
@param num_operands: 操作数总数
|
|
|
|
|
@return: (表达式部分列表, 是否已添加特殊操作标记)
|
|
|
|
|
"""
|
|
|
|
|
rand_val = random.random()
|
|
|
|
|
# 修改这里的概率,增加开根号和平方的出现频率
|
|
|
|
|
if not special_added and rand_val < 0.6:
|
|
|
|
|
# 添加特殊操作(平方或开根号)
|
|
|
|
|
expression_parts, special_added = self._add_middle_special_operation()
|
|
|
|
|
else:
|
|
|
|
|
# 普通操作数
|
|
|
|
|
expression_parts.append(str(random.randint(1, 100)))
|
|
|
|
|
return expression_parts, special_added
|
|
|
|
|
|
|
|
|
|
return expression_parts, special_added
|
|
|
|
|
|
|
|
|
|
def _add_middle_special_operation(self) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
添加初中特殊操作(平方或开根号)
|
|
|
|
|
|
|
|
|
|
@return: (表达式部分列表, 是否已添加特殊操作标记)
|
|
|
|
|
"""
|
|
|
|
|
choice = random.choice([0, 1]) # 平方和开根号的概率相等
|
|
|
|
|
expression_parts = []
|
|
|
|
|
if choice == 0:
|
|
|
|
|
# 添加平方
|
|
|
|
|
base = random.randint(1, 100)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
else:
|
|
|
|
|
# 添加开根号
|
|
|
|
|
square = random.randint(1, 10)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
return expression_parts, True
|
|
|
|
|
|
|
|
|
|
def _prepare_middle_expression_for_eval(self, expression_str: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
准备初中题目表达式用于eval计算
|
|
|
|
|
|
|
|
|
|
@param expression_str: 原始表达式字符串
|
|
|
|
|
@return: 可用于eval计算的表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
# 替换表达式中的函数以便计算
|
|
|
|
|
eval_expr = expression_str.replace('²', '**2')
|
|
|
|
|
# 使用正则表达式正确处理平方根
|
|
|
|
|
eval_expr = re.sub(r'√(\d+)',
|
|
|
|
|
r'math.sqrt(\1)',
|
|
|
|
|
eval_expr)
|
|
|
|
|
return eval_expr
|
|
|
|
|
|
|
|
|
|
def fix_expression_syntax(self, expression: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
修复表达式语法问题
|
|
|
|
|
@ -266,8 +368,6 @@ class MiddleQuestionGenerator(QuestionGenerator):
|
|
|
|
|
expression = re.sub(r'(²)([√])', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(\))(\d)', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(\d)(\()', r'\1*\2', expression)
|
|
|
|
|
# 修复根号表达式显示,确保根号符号正确显示
|
|
|
|
|
expression = re.sub(r'√(\d+)', r'√\1', expression)
|
|
|
|
|
return expression
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -279,110 +379,34 @@ class HighQuestionGenerator(QuestionGenerator):
|
|
|
|
|
def generate_question(self) -> Expression:
|
|
|
|
|
"""
|
|
|
|
|
生成高中题目
|
|
|
|
|
|
|
|
|
|
@return: 数学表达式
|
|
|
|
|
"""
|
|
|
|
|
max_attempts = 100
|
|
|
|
|
for _ in range(max_attempts):
|
|
|
|
|
expression_parts = []
|
|
|
|
|
|
|
|
|
|
# 确保至少有一个三角函数
|
|
|
|
|
has_trig_function = random.random() < 0.8
|
|
|
|
|
|
|
|
|
|
# 生成操作数数量(2-4个)
|
|
|
|
|
num_operands = random.randint(2, 4)
|
|
|
|
|
|
|
|
|
|
for i in range(num_operands):
|
|
|
|
|
if has_trig_function and i == 0:
|
|
|
|
|
# 第一个操作数有较高概率是三角函数
|
|
|
|
|
if random.random() < 0.33:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"sin({angle}°)")
|
|
|
|
|
elif random.random() < 0.5:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"cos({angle}°)")
|
|
|
|
|
else:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"tan({angle}°)")
|
|
|
|
|
else:
|
|
|
|
|
# 其他操作数
|
|
|
|
|
rand_val = random.random()
|
|
|
|
|
if rand_val < 0.1 and has_trig_function:
|
|
|
|
|
# 添加三角函数
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"sin({angle}°)")
|
|
|
|
|
elif rand_val < 0.2 and has_trig_function:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"cos({angle}°)")
|
|
|
|
|
elif rand_val < 0.3 and has_trig_function:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"tan({angle}°)")
|
|
|
|
|
elif rand_val < 0.55:
|
|
|
|
|
# 普通数字
|
|
|
|
|
expression_parts.append(str(random.randint(1, 20)))
|
|
|
|
|
elif rand_val < 0.7:
|
|
|
|
|
# 平方
|
|
|
|
|
base = random.randint(1, 10)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
else:
|
|
|
|
|
# 开根号
|
|
|
|
|
square = random.randint(1, 10)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
|
|
|
|
|
# 添加运算符(除了最后一个操作数)
|
|
|
|
|
if i < num_operands - 1:
|
|
|
|
|
expression_parts.append(random.choice(['+', '-', '*', '/']))
|
|
|
|
|
|
|
|
|
|
expression_str = ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
# 处理可能的语法问题
|
|
|
|
|
expression_str = self._generate_high_expression()
|
|
|
|
|
expression_str = self.fix_expression_syntax(expression_str)
|
|
|
|
|
|
|
|
|
|
# 计算结果
|
|
|
|
|
try:
|
|
|
|
|
# 替换表达式中的函数以便计算
|
|
|
|
|
eval_expr = expression_str.replace('²', '**2').replace('√', 'math.sqrt')
|
|
|
|
|
# 修复:确保正确的替换顺序,先替换角度制三角函数
|
|
|
|
|
eval_expr = re.sub(r'sin\((\d+)°\)', r'math.sin(math.radians(\1))', eval_expr)
|
|
|
|
|
eval_expr = re.sub(r'cos\((\d+)°\)', r'math.cos(math.radians(\1))', eval_expr)
|
|
|
|
|
eval_expr = re.sub(r'tan\((\d+)°\)', r'math.tan(math.radians(\1))', eval_expr)
|
|
|
|
|
|
|
|
|
|
eval_expr = self._prepare_high_expression_for_eval(expression_str)
|
|
|
|
|
result = eval(eval_expr)
|
|
|
|
|
|
|
|
|
|
# 确保结果是合理的
|
|
|
|
|
if isinstance(result, (int, float)) and abs(result) < 1000 and not math.isnan(result) and not math.isinf(result):
|
|
|
|
|
# 格式化结果
|
|
|
|
|
if isinstance(result, float) and abs(result - round(result, 10)) < 1e-10:
|
|
|
|
|
result = int(round(result))
|
|
|
|
|
# 特殊处理常见的三角函数值,使其更加准确
|
|
|
|
|
elif isinstance(result, float):
|
|
|
|
|
# 对于常见的三角函数值进行舍入处理
|
|
|
|
|
if abs(result - 0.5) < 1e-10: # sin(30°) = 0.5
|
|
|
|
|
result = 0.5
|
|
|
|
|
elif abs(result - 0.7071067811865476) < 1e-10: # sin(45°) = cos(45°) ≈ 0.707
|
|
|
|
|
result = round(result, 10)
|
|
|
|
|
elif abs(result - 0.8660254037844386) < 1e-10: # sin(60°) ≈ 0.866
|
|
|
|
|
result = round(result, 10)
|
|
|
|
|
elif abs(result - 0.5773502691896257) < 1e-10: # tan(30°) ≈ 0.577
|
|
|
|
|
result = round(result, 10)
|
|
|
|
|
elif abs(result - 1.7320508075688772) < 1e-10: # tan(60°) ≈ 1.732
|
|
|
|
|
result = round(result, 10)
|
|
|
|
|
elif abs(result - 1.0) < 1e-10: # tan(45°) = 1, sin(90°) = 1
|
|
|
|
|
result = 1.0
|
|
|
|
|
|
|
|
|
|
if (isinstance(result, (int, float)) and
|
|
|
|
|
abs(result) < 1000 and
|
|
|
|
|
not math.isnan(result) and
|
|
|
|
|
not math.isinf(result)):
|
|
|
|
|
# 格式化结果,保留两位小数
|
|
|
|
|
result = self._format_high_result(result)
|
|
|
|
|
expr = Expression(expression_str, result)
|
|
|
|
|
# 检查是否已生成过相同题目
|
|
|
|
|
if str(expr) not in self.generated_questions:
|
|
|
|
|
self.generated_questions.add(str(expr))
|
|
|
|
|
# 将表达式中的乘号和除号替换为更易读的形式
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×').replace('/', '÷')
|
|
|
|
|
readable_expr_str = expression_str.replace('*', '×')
|
|
|
|
|
readable_expr_str = readable_expr_str.replace('/', '÷')
|
|
|
|
|
readable_expr = Expression(readable_expr_str, result)
|
|
|
|
|
return readable_expr
|
|
|
|
|
except:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 如果无法生成有效题目,返回默认题目
|
|
|
|
|
expr = Expression("sin(30°)", 0.5)
|
|
|
|
|
self.generated_questions.add(str(expr))
|
|
|
|
|
readable_expr = Expression("sin(30°)", 0.5)
|
|
|
|
|
@ -391,6 +415,189 @@ class HighQuestionGenerator(QuestionGenerator):
|
|
|
|
|
readable_expr = Expression(readable_expr_str, 0.5)
|
|
|
|
|
return readable_expr
|
|
|
|
|
|
|
|
|
|
def _generate_high_expression(self) -> str:
|
|
|
|
|
"""
|
|
|
|
|
生成高中题目表达式字符串
|
|
|
|
|
|
|
|
|
|
@return: 表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
expression_parts = []
|
|
|
|
|
|
|
|
|
|
# 生成操作数数量(2-4个)
|
|
|
|
|
num_operands = random.randint(2, 5)
|
|
|
|
|
|
|
|
|
|
# 标记是否已添加三角函数
|
|
|
|
|
trig_added = False
|
|
|
|
|
|
|
|
|
|
# 生成表达式部分
|
|
|
|
|
expression_parts, trig_added = self._build_high_expression_parts(
|
|
|
|
|
expression_parts, num_operands, trig_added)
|
|
|
|
|
|
|
|
|
|
return ''.join(expression_parts)
|
|
|
|
|
|
|
|
|
|
def _build_high_expression_parts(self, expression_parts: List[str],
|
|
|
|
|
num_operands: int,
|
|
|
|
|
trig_added: bool) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
构建高中表达式部分
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@param num_operands: 操作数数量
|
|
|
|
|
@param trig_added: 是否已添加三角函数标记
|
|
|
|
|
@return: (表达式部分列表, 是否已添加三角函数标记)
|
|
|
|
|
"""
|
|
|
|
|
for i in range(num_operands):
|
|
|
|
|
# 确保至少添加一个三角函数
|
|
|
|
|
if not trig_added and i == num_operands - 1:
|
|
|
|
|
# 如果还没添加三角函数,强制在最后一个操作数添加
|
|
|
|
|
expression_parts, trig_added = self._add_forced_trig_function(
|
|
|
|
|
expression_parts)
|
|
|
|
|
else:
|
|
|
|
|
# 其他操作数
|
|
|
|
|
expression_parts, trig_added = self._add_high_operand_or_operation(
|
|
|
|
|
expression_parts, trig_added)
|
|
|
|
|
|
|
|
|
|
# 添加运算符(除了最后一个操作数)
|
|
|
|
|
if i < num_operands - 1:
|
|
|
|
|
expression_parts.append(random.choice(['+', '-', '*', '/']))
|
|
|
|
|
|
|
|
|
|
return expression_parts, trig_added
|
|
|
|
|
|
|
|
|
|
def _add_forced_trig_function(self, expression_parts: List[str]) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
强制添加三角函数
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@return: (表达式部分列表, 是否已添加三角函数标记)
|
|
|
|
|
"""
|
|
|
|
|
choice = random.randint(0, 2)
|
|
|
|
|
if choice == 0:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60]) # 不包含90度
|
|
|
|
|
expression_parts.append(f"sin({angle}°)")
|
|
|
|
|
elif choice == 1:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"cos({angle}°)")
|
|
|
|
|
else:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60]) # 不包含90度
|
|
|
|
|
expression_parts.append(f"tan({angle}°)")
|
|
|
|
|
return expression_parts, True
|
|
|
|
|
|
|
|
|
|
def _add_high_operand_or_operation(self, expression_parts: List[str],
|
|
|
|
|
trig_added: bool) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
添加高中操作数或操作
|
|
|
|
|
|
|
|
|
|
@param expression_parts: 表达式部分列表
|
|
|
|
|
@param trig_added: 是否已添加三角函数标记
|
|
|
|
|
@return: (表达式部分列表, 是否已添加三角函数标记)
|
|
|
|
|
"""
|
|
|
|
|
rand_val = random.random()
|
|
|
|
|
if not trig_added and rand_val < 0.4:
|
|
|
|
|
# 添加三角函数
|
|
|
|
|
return self._add_trig_function()
|
|
|
|
|
elif rand_val < 0.65:
|
|
|
|
|
# 普通数字
|
|
|
|
|
expression_parts.append(str(random.randint(1, 20)))
|
|
|
|
|
return expression_parts, trig_added
|
|
|
|
|
elif rand_val < 0.8:
|
|
|
|
|
# 平方
|
|
|
|
|
base = random.randint(1, 10)
|
|
|
|
|
expression_parts.append(f"{base}²")
|
|
|
|
|
return expression_parts, trig_added
|
|
|
|
|
else:
|
|
|
|
|
# 开根号
|
|
|
|
|
square = random.randint(1, 10)
|
|
|
|
|
value = square * square
|
|
|
|
|
expression_parts.append(f"√{value}")
|
|
|
|
|
return expression_parts, trig_added
|
|
|
|
|
|
|
|
|
|
def _add_trig_function(self) -> tuple:
|
|
|
|
|
"""
|
|
|
|
|
添加三角函数
|
|
|
|
|
|
|
|
|
|
@return: (表达式部分列表, 是否已添加三角函数标记)
|
|
|
|
|
"""
|
|
|
|
|
expression_parts = []
|
|
|
|
|
func_choice = random.randint(0, 2)
|
|
|
|
|
if func_choice == 0:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60]) # 不包含90度
|
|
|
|
|
expression_parts.append(f"sin({angle}°)")
|
|
|
|
|
elif func_choice == 1:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60, 90])
|
|
|
|
|
expression_parts.append(f"cos({angle}°)")
|
|
|
|
|
else:
|
|
|
|
|
angle = random.choice([0, 30, 45, 60]) # 不包含90度
|
|
|
|
|
expression_parts.append(f"tan({angle}°)")
|
|
|
|
|
return expression_parts, True
|
|
|
|
|
|
|
|
|
|
def _prepare_high_expression_for_eval(self, expression_str: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
准备高中题目表达式用于eval计算
|
|
|
|
|
|
|
|
|
|
@param expression_str: 原始表达式字符串
|
|
|
|
|
@return: 可用于eval计算的表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
eval_expr = self._replace_powers_and_roots(expression_str)
|
|
|
|
|
# 修复:确保正确的替换顺序,先替换角度制三角函数
|
|
|
|
|
eval_expr = self._replace_trig_functions(eval_expr)
|
|
|
|
|
return eval_expr
|
|
|
|
|
|
|
|
|
|
def _replace_powers_and_roots(self, expression_str: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
替换幂运算和开根号符号
|
|
|
|
|
|
|
|
|
|
@param expression_str: 原始表达式字符串
|
|
|
|
|
@return: 替换后的表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
eval_expr = expression_str.replace('²', '**2')
|
|
|
|
|
eval_expr = eval_expr.replace('√', 'math.sqrt')
|
|
|
|
|
return eval_expr
|
|
|
|
|
|
|
|
|
|
def _replace_trig_functions(self, eval_expr: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
替换三角函数
|
|
|
|
|
|
|
|
|
|
@param eval_expr: 表达式字符串
|
|
|
|
|
@return: 替换三角函数后的表达式字符串
|
|
|
|
|
"""
|
|
|
|
|
eval_expr = re.sub(r'sin\((\d+)°\)',
|
|
|
|
|
r'math.sin(math.radians(\1))',
|
|
|
|
|
eval_expr)
|
|
|
|
|
eval_expr = re.sub(r'cos\((\d+)°\)',
|
|
|
|
|
r'math.cos(math.radians(\1))',
|
|
|
|
|
eval_expr)
|
|
|
|
|
eval_expr = re.sub(r'tan\((\d+)°\)',
|
|
|
|
|
r'math.tan(math.radians(\1))',
|
|
|
|
|
eval_expr)
|
|
|
|
|
return eval_expr
|
|
|
|
|
|
|
|
|
|
def _format_high_result(self, result: float) -> float:
|
|
|
|
|
"""
|
|
|
|
|
格式化高中题目计算结果
|
|
|
|
|
|
|
|
|
|
@param result: 计算结果
|
|
|
|
|
@return: 格式化后的结果
|
|
|
|
|
"""
|
|
|
|
|
if isinstance(result, float):
|
|
|
|
|
# 特殊处理常见的三角函数值,使其更加准确
|
|
|
|
|
if abs(result - 0.5) < 1e-10: # sin(30°) = 0.5
|
|
|
|
|
result = 0.5
|
|
|
|
|
elif abs(result - 0.7071067811865476) < 1e-10: # sin(45°) = cos(45°) ≈ 0.707
|
|
|
|
|
result = round(result, 2)
|
|
|
|
|
elif abs(result - 0.8660254037844386) < 1e-10: # sin(60°) ≈ 0.866
|
|
|
|
|
result = round(result, 2)
|
|
|
|
|
elif abs(result - 0.5773502691896257) < 1e-10: # tan(30°) ≈ 0.577
|
|
|
|
|
result = round(result, 2)
|
|
|
|
|
elif abs(result - 1.7320508075688772) < 1e-10: # tan(60°) ≈ 1.732
|
|
|
|
|
result = round(result, 2)
|
|
|
|
|
elif abs(result - 1.0) < 1e-10: # tan(45°) = 1, sin(90°) = 1
|
|
|
|
|
result = 1.0
|
|
|
|
|
else:
|
|
|
|
|
# 保留两位小数
|
|
|
|
|
result = round(result, 2)
|
|
|
|
|
# 整数保持不变
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def fix_expression_syntax(self, expression: str) -> str:
|
|
|
|
|
"""
|
|
|
|
|
修复表达式语法问题
|
|
|
|
|
@ -399,10 +606,22 @@ class HighQuestionGenerator(QuestionGenerator):
|
|
|
|
|
@return: 修复后的表达式
|
|
|
|
|
"""
|
|
|
|
|
# 确保函数调用之间有运算符
|
|
|
|
|
expression = re.sub(r'(\d)([sincostan√])', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(²)([sincostan√])', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(sqrt\(\d+\))(\d)', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(\))(\d)', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'(\d)(\()', r'\1*\2', expression)
|
|
|
|
|
expression = re.sub(r'°\)(\d)', r'°)*\1', expression)
|
|
|
|
|
expression = re.sub(r'(\d)([sincostan√])',
|
|
|
|
|
r'\1*\2',
|
|
|
|
|
expression)
|
|
|
|
|
expression = re.sub(r'(²)([sincostan√])',
|
|
|
|
|
r'\1*\2',
|
|
|
|
|
expression)
|
|
|
|
|
expression = re.sub(r'(sqrt\(\d+\))(\d)',
|
|
|
|
|
r'\1*\2',
|
|
|
|
|
expression)
|
|
|
|
|
expression = re.sub(r'(\))(\d)',
|
|
|
|
|
r'\1*\2',
|
|
|
|
|
expression)
|
|
|
|
|
expression = re.sub(r'(\d)(\()',
|
|
|
|
|
r'\1*\2',
|
|
|
|
|
expression)
|
|
|
|
|
expression = re.sub(r'°\)(\d)',
|
|
|
|
|
r'°)*\1',
|
|
|
|
|
expression)
|
|
|
|
|
return expression
|