Compare commits
4 Commits
4a5749a1f4
...
e6afe6eca1
Author | SHA1 | Date | |
---|---|---|---|
e6afe6eca1
|
|||
2110961f32
|
|||
4d46849426
|
|||
d0dfe2f236
|
289
lib/solver.dart
289
lib/solver.dart
@@ -59,15 +59,33 @@ class SolverService {
|
|||||||
stepNumber: 1,
|
stepNumber: 1,
|
||||||
title: '表达式求值',
|
title: '表达式求值',
|
||||||
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
|
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
|
||||||
formula: input,
|
formula: '\$\$$input\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
GrammarParser p = GrammarParser();
|
// 检查是否为特殊三角函数值,可以返回精确结果
|
||||||
Expression exp = p.parse(input);
|
final exactTrigResult = _getExactTrigResult(input);
|
||||||
final result = RealEvaluator().evaluate(exp);
|
if (exactTrigResult != null) {
|
||||||
|
return CalculationResult(
|
||||||
|
steps: steps,
|
||||||
|
finalAnswer: '\$\$$exactTrigResult\$\$',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CalculationResult(steps: steps, finalAnswer: result.toString());
|
// 预处理输入,将三角函数的参数从度转换为弧度
|
||||||
|
String processedInput = _convertTrigToRadians(input);
|
||||||
|
|
||||||
|
GrammarParser p = GrammarParser();
|
||||||
|
Expression exp = p.parse(processedInput);
|
||||||
|
final result = RealEvaluator().evaluate(exp).toDouble();
|
||||||
|
|
||||||
|
// 尝试将结果格式化为几倍根号的形式
|
||||||
|
final formattedResult = _formatSqrtResult(result);
|
||||||
|
|
||||||
|
return CalculationResult(
|
||||||
|
steps: steps,
|
||||||
|
finalAnswer: '\$\$$formattedResult\$\$',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 2. 求解一元一次方程
|
/// 2. 求解一元一次方程
|
||||||
@@ -316,12 +334,12 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2)
|
|||||||
explanation: '为了消去变量 y,将方程(1)两边乘以 $b2,方程(2)两边乘以 $b1。',
|
explanation: '为了消去变量 y,将方程(1)两边乘以 $b2,方程(2)两边乘以 $b1。',
|
||||||
formula:
|
formula:
|
||||||
'''
|
'''
|
||||||
|
\$\$
|
||||||
\\begin{cases}
|
\\begin{cases}
|
||||||
${newA1}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC1 & (3) \\
|
${newA1}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC1 & (3) \\\\
|
||||||
${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
||||||
\\end{cases}
|
\\end{cases}
|
||||||
|
\$\$
|
||||||
''',
|
''',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -360,14 +378,14 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
|||||||
explanation: '将 x = $x 代入原方程(2)中。',
|
explanation: '将 x = $x 代入原方程(2)中。',
|
||||||
formula:
|
formula:
|
||||||
'''
|
'''
|
||||||
|
\$\$
|
||||||
\\begin{aligned}
|
\\begin{aligned}
|
||||||
$a2($x) + ${b2}y &= $c2 \\\\
|
$a2($x) + ${b2}y &= $c2 \\\\
|
||||||
${a2 * x} + ${b2}y &= $c2 \\\\
|
${a2 * x} + ${b2}y &= $c2 \\\\
|
||||||
${b2}y &= $c2 - ${a2 * x} \\\\
|
${b2}y &= $c2 - ${a2 * x} \\\\
|
||||||
${b2}y &= ${c2 - a2 * x}
|
${b2}y &= ${c2 - a2 * x}
|
||||||
\\end{aligned}
|
\\end{aligned}
|
||||||
|
\$\$
|
||||||
''',
|
''',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -394,14 +412,14 @@ ${b2}y &= ${c2 - a2 * x}
|
|||||||
explanation: '将 x = $x 代入原方程(1)中。',
|
explanation: '将 x = $x 代入原方程(1)中。',
|
||||||
formula:
|
formula:
|
||||||
'''
|
'''
|
||||||
|
\$\$
|
||||||
\\begin{aligned}
|
\\begin{aligned}
|
||||||
$a1($x) + ${b1}y &= $c1 \\\\
|
$a1($x) + ${b1}y &= $c1 \\\\
|
||||||
${a1 * x} + ${b1}y &= $c1 \\\\
|
${a1 * x} + ${b1}y &= $c1 \\\\
|
||||||
${b1}y &= $c1 - ${a1 * x} \\\\
|
${b1}y &= $c1 - ${a1 * x} \\\\
|
||||||
${b1}y &= ${c1 - a1 * x}
|
${b1}y &= ${c1 - a1 * x}
|
||||||
\\end{aligned}
|
\\end{aligned}
|
||||||
|
\$\$
|
||||||
''',
|
''',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -422,6 +440,198 @@ ${b1}y &= ${c1 - a1 * x}
|
|||||||
|
|
||||||
/// ---- 辅助函数 ----
|
/// ---- 辅助函数 ----
|
||||||
|
|
||||||
|
/// 获取精确三角函数结果
|
||||||
|
String? _getExactTrigResult(String input) {
|
||||||
|
final cleanInput = input.replaceAll(' ', '').toLowerCase();
|
||||||
|
|
||||||
|
// 匹配 sin(角度) 模式
|
||||||
|
final sinMatch = RegExp(r'^sin\((\d+(?:\+\d+)*)\)$').firstMatch(cleanInput);
|
||||||
|
if (sinMatch != null) {
|
||||||
|
final angleExpr = sinMatch.group(1)!;
|
||||||
|
final angle = _evaluateAngleExpression(angleExpr);
|
||||||
|
if (angle != null) {
|
||||||
|
return _getSinExactValue(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配 cos(角度) 模式
|
||||||
|
final cosMatch = RegExp(r'^cos\((\d+(?:\+\d+)*)\)$').firstMatch(cleanInput);
|
||||||
|
if (cosMatch != null) {
|
||||||
|
final angleExpr = cosMatch.group(1)!;
|
||||||
|
final angle = _evaluateAngleExpression(angleExpr);
|
||||||
|
if (angle != null) {
|
||||||
|
return _getCosExactValue(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配 tan(角度) 模式
|
||||||
|
final tanMatch = RegExp(r'^tan\((\d+(?:\+\d+)*)\)$').firstMatch(cleanInput);
|
||||||
|
if (tanMatch != null) {
|
||||||
|
final angleExpr = tanMatch.group(1)!;
|
||||||
|
final angle = _evaluateAngleExpression(angleExpr);
|
||||||
|
if (angle != null) {
|
||||||
|
return _getTanExactValue(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算角度表达式(如 30+45 = 75)
|
||||||
|
int? _evaluateAngleExpression(String expr) {
|
||||||
|
final parts = expr.split('+');
|
||||||
|
int sum = 0;
|
||||||
|
for (final part in parts) {
|
||||||
|
final num = int.tryParse(part.trim());
|
||||||
|
if (num == null) return null;
|
||||||
|
sum += num;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 sin 的精确值
|
||||||
|
String? _getSinExactValue(int angle) {
|
||||||
|
// 标准化角度到 0-360 度
|
||||||
|
final normalizedAngle = angle % 360;
|
||||||
|
|
||||||
|
switch (normalizedAngle) {
|
||||||
|
case 0:
|
||||||
|
case 360:
|
||||||
|
return '0';
|
||||||
|
case 30:
|
||||||
|
return '\\frac{1}{2}';
|
||||||
|
case 45:
|
||||||
|
return '\\frac{\\sqrt{2}}{2}';
|
||||||
|
case 60:
|
||||||
|
return '\\frac{\\sqrt{3}}{2}';
|
||||||
|
case 75:
|
||||||
|
return '1 + \\frac{\\sqrt{2}}{2}';
|
||||||
|
case 90:
|
||||||
|
return '1';
|
||||||
|
case 120:
|
||||||
|
return '\\frac{\\sqrt{3}}{2}';
|
||||||
|
case 135:
|
||||||
|
return '\\frac{\\sqrt{2}}{2}';
|
||||||
|
case 150:
|
||||||
|
return '\\frac{1}{2}';
|
||||||
|
case 180:
|
||||||
|
return '0';
|
||||||
|
case 210:
|
||||||
|
return '-\\frac{1}{2}';
|
||||||
|
case 225:
|
||||||
|
return '-\\frac{\\sqrt{2}}{2}';
|
||||||
|
case 240:
|
||||||
|
return '-\\frac{\\sqrt{3}}{2}';
|
||||||
|
case 270:
|
||||||
|
return '-1';
|
||||||
|
case 300:
|
||||||
|
return '-\\frac{\\sqrt{3}}{2}';
|
||||||
|
case 315:
|
||||||
|
return '-\\frac{\\sqrt{2}}{2}';
|
||||||
|
case 330:
|
||||||
|
return '-\\frac{1}{2}';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 cos 的精确值
|
||||||
|
String? _getCosExactValue(int angle) {
|
||||||
|
// cos(angle) = sin(90 - angle)
|
||||||
|
final complementaryAngle = 90 - angle;
|
||||||
|
return _getSinExactValue(complementaryAngle.abs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 tan 的精确值
|
||||||
|
String? _getTanExactValue(int angle) {
|
||||||
|
// tan(angle) = sin(angle) / cos(angle)
|
||||||
|
final sinValue = _getSinExactValue(angle);
|
||||||
|
final cosValue = _getCosExactValue(angle);
|
||||||
|
|
||||||
|
if (sinValue != null && cosValue != null) {
|
||||||
|
if (cosValue == '0') return null; // 未定义
|
||||||
|
return '\\frac{$sinValue}{$cosValue}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将三角函数的参数从度转换为弧度
|
||||||
|
String _convertTrigToRadians(String input) {
|
||||||
|
String result = input;
|
||||||
|
|
||||||
|
// 正则表达式匹配三角函数调用,如 sin(30), cos(45), tan(60)
|
||||||
|
final trigPattern = RegExp(
|
||||||
|
r'(sin|cos|tan|asin|acos|atan)\s*\(\s*([^)]+)\s*\)',
|
||||||
|
caseSensitive: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
result = result.replaceAllMapped(trigPattern, (match) {
|
||||||
|
final func = match.group(1)!;
|
||||||
|
final arg = match.group(2)!;
|
||||||
|
|
||||||
|
// 如果参数已经是弧度相关的表达式(包含 pi 或 π),则不转换
|
||||||
|
if (arg.contains('pi') || arg.contains('π') || arg.contains('rad')) {
|
||||||
|
return '$func($arg)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将度数转换为弧度:度 * π / 180
|
||||||
|
return '$func(($arg)*($pi/180))';
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将数值结果格式化为几倍根号的形式
|
||||||
|
String _formatSqrtResult(double result) {
|
||||||
|
// 处理负数
|
||||||
|
if (result < 0) {
|
||||||
|
return '-${_formatSqrtResult(-result)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理零
|
||||||
|
if (result == 0) return '0';
|
||||||
|
|
||||||
|
// 检查是否接近整数
|
||||||
|
final rounded = result.round();
|
||||||
|
if ((result - rounded).abs() < 1e-10) {
|
||||||
|
return rounded.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 result 的平方,看它是否接近整数
|
||||||
|
final squared = result * result;
|
||||||
|
final squaredRounded = squared.round();
|
||||||
|
|
||||||
|
// 如果 squared 接近整数,说明 result 是某个数的平方根
|
||||||
|
if ((squared - squaredRounded).abs() < 1e-6) {
|
||||||
|
// 寻找最大的完全平方数因子
|
||||||
|
int maxSquareFactor = 1;
|
||||||
|
for (int i = 2; i * i <= squaredRounded; i++) {
|
||||||
|
if (squaredRounded % (i * i) == 0) {
|
||||||
|
maxSquareFactor = i * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final coefficient = sqrt(maxSquareFactor).round();
|
||||||
|
final remaining = squaredRounded ~/ maxSquareFactor;
|
||||||
|
|
||||||
|
if (remaining == 1) {
|
||||||
|
// 完全平方数,直接返回系数
|
||||||
|
return coefficient.toString();
|
||||||
|
} else if (coefficient == 1) {
|
||||||
|
return '\\sqrt{$remaining}';
|
||||||
|
} else {
|
||||||
|
return '$coefficient\\sqrt{$remaining}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是平方根的结果,返回原始数值(保留几位小数)
|
||||||
|
return result
|
||||||
|
.toStringAsFixed(6)
|
||||||
|
.replaceAll(RegExp(r'\.0+$'), '')
|
||||||
|
.replaceAll(RegExp(r'\.$'), '');
|
||||||
|
}
|
||||||
|
|
||||||
String _expandExpressions(String input) {
|
String _expandExpressions(String input) {
|
||||||
String result = input;
|
String result = input;
|
||||||
int maxIterations = 10; // Prevent infinite loops
|
int maxIterations = 10; // Prevent infinite loops
|
||||||
@@ -544,29 +754,68 @@ ${b1}y &= ${c1 - a1 * x}
|
|||||||
|
|
||||||
Map<int, double> _parsePolynomial(String side) {
|
Map<int, double> _parsePolynomial(String side) {
|
||||||
final coeffs = <int, double>{};
|
final coeffs = <int, double>{};
|
||||||
|
// 扩展模式以支持 sqrt 函数
|
||||||
final pattern = RegExp(
|
final pattern = RegExp(
|
||||||
r'([+-]?(?:\d*\.?\d*)?)x(?:\^(\d+))?|([+-]?\d*\.?\d+)',
|
r'([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))x(?:\^(\d+))?|([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))',
|
||||||
);
|
);
|
||||||
var s = side.startsWith('+') || side.startsWith('-') ? side : '+$side';
|
var s = side.startsWith('+') || side.startsWith('-') ? side : '+$side';
|
||||||
|
|
||||||
for (final match in pattern.allMatches(s)) {
|
for (final match in pattern.allMatches(s)) {
|
||||||
if (match.group(3) != null) {
|
if (match.group(3) != null) {
|
||||||
coeffs[0] = (coeffs[0] ?? 0) + double.parse(match.group(3)!);
|
// 常数项
|
||||||
|
final constStr = match.group(3)!;
|
||||||
|
final constValue = _parseCoefficientWithSqrt(constStr);
|
||||||
|
coeffs[0] = (coeffs[0] ?? 0) + constValue;
|
||||||
} else {
|
} else {
|
||||||
|
// x 的幂次项
|
||||||
int power = match.group(2) != null ? int.parse(match.group(2)!) : 1;
|
int power = match.group(2) != null ? int.parse(match.group(2)!) : 1;
|
||||||
String coeffStr = match.group(1) ?? '+';
|
String coeffStr = match.group(1) ?? '+';
|
||||||
double coeff = 1.0;
|
final coeff = _parseCoefficientWithSqrt(coeffStr);
|
||||||
if (coeffStr.isNotEmpty && coeffStr != '+') {
|
|
||||||
coeff = coeffStr == '-' ? -1.0 : double.parse(coeffStr);
|
|
||||||
} else if (coeffStr == '-') {
|
|
||||||
coeff = -1.0;
|
|
||||||
}
|
|
||||||
coeffs[power] = (coeffs[power] ?? 0) + coeff;
|
coeffs[power] = (coeffs[power] ?? 0) + coeff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return coeffs;
|
return coeffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 解析包含 sqrt 函数的系数
|
||||||
|
double _parseCoefficientWithSqrt(String coeffStr) {
|
||||||
|
if (coeffStr.isEmpty || coeffStr == '+') return 1.0;
|
||||||
|
if (coeffStr == '-') return -1.0;
|
||||||
|
|
||||||
|
// 检查是否包含 sqrt 函数
|
||||||
|
final sqrtMatch = RegExp(r'sqrt\((\d+)\)').firstMatch(coeffStr);
|
||||||
|
if (sqrtMatch != null) {
|
||||||
|
final innerValue = int.parse(sqrtMatch.group(1)!);
|
||||||
|
|
||||||
|
// 对于完全平方数,直接返回整数结果
|
||||||
|
final sqrtValue = sqrt(innerValue.toDouble());
|
||||||
|
final rounded = sqrtValue.round();
|
||||||
|
if ((sqrtValue - rounded).abs() < 1e-10) {
|
||||||
|
// 检查是否有系数
|
||||||
|
final coeffPart = coeffStr.replaceFirst(sqrtMatch.group(0)!, '');
|
||||||
|
if (coeffPart.isEmpty) return rounded.toDouble();
|
||||||
|
if (coeffPart == '-') return -rounded.toDouble();
|
||||||
|
|
||||||
|
final coeff = double.parse(coeffPart);
|
||||||
|
return coeff * rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于非完全平方数,计算数值但保持高精度
|
||||||
|
final nonPerfectSqrtValue = sqrt(innerValue.toDouble());
|
||||||
|
|
||||||
|
// 检查是否有系数
|
||||||
|
final coeffPart = coeffStr.replaceFirst(sqrtMatch.group(0)!, '');
|
||||||
|
if (coeffPart.isEmpty) return nonPerfectSqrtValue;
|
||||||
|
if (coeffPart == '-') return -nonPerfectSqrtValue;
|
||||||
|
|
||||||
|
final coeff = double.parse(coeffPart);
|
||||||
|
return coeff * nonPerfectSqrtValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通数值
|
||||||
|
return double.parse(coeffStr);
|
||||||
|
}
|
||||||
|
|
||||||
List<double> _parseTwoVariableLinear(String equation) {
|
List<double> _parseTwoVariableLinear(String equation) {
|
||||||
final parts = equation.split('=');
|
final parts = equation.split('=');
|
||||||
if (parts.length != 2) throw Exception("方程 $equation 格式错误");
|
if (parts.length != 2) throw Exception("方程 $equation 格式错误");
|
||||||
|
Reference in New Issue
Block a user