💄 Improve accurate of sqrt calculation
This commit is contained in:
463
lib/solver.dart
463
lib/solver.dart
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:flutter/foundation.dart'; // For kDebugMode
|
import 'package:flutter/foundation.dart'; // For kDebugMode
|
||||||
import 'package:math_expressions/math_expressions.dart';
|
import 'package:math_expressions/math_expressions.dart';
|
||||||
|
import 'package:rational/rational.dart';
|
||||||
import 'models/calculation_step.dart';
|
import 'models/calculation_step.dart';
|
||||||
|
|
||||||
/// 帮助解析一元一次方程 ax+b=cx+d 的辅助类
|
/// 帮助解析一元一次方程 ax+b=cx+d 的辅助类
|
||||||
@@ -103,8 +104,8 @@ class SolverService {
|
|||||||
final parts = _parseLinearEquation(input);
|
final parts = _parseLinearEquation(input);
|
||||||
final a = parts.a, b = parts.b, c = parts.c, d = parts.d;
|
final a = parts.a, b = parts.b, c = parts.c, d = parts.d;
|
||||||
|
|
||||||
final newA = a - c;
|
final newA = _rationalFromDouble(a) - _rationalFromDouble(c);
|
||||||
final newD = d - b;
|
final newD = _rationalFromDouble(d) - _rationalFromDouble(b);
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
@@ -121,14 +122,15 @@ class SolverService {
|
|||||||
stepNumber: 2,
|
stepNumber: 2,
|
||||||
title: '合并同类项',
|
title: '合并同类项',
|
||||||
explanation: '合并等式两边的项。',
|
explanation: '合并等式两边的项。',
|
||||||
formula: '\$\$${newA}x = $newD\$\$',
|
formula:
|
||||||
|
'\$\$${newA.toDouble().toStringAsFixed(4)}x = ${newD.toDouble().toStringAsFixed(4)}\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newA == 0) {
|
if (newA == Rational.zero) {
|
||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer: newD == 0 ? '有无穷多解' : '无解',
|
finalAnswer: newD == Rational.zero ? '有无穷多解' : '无解',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +154,29 @@ class SolverService {
|
|||||||
final eqParts = input.split('=');
|
final eqParts = input.split('=');
|
||||||
if (eqParts.length != 2) throw Exception("方程格式错误,应包含一个 '='。");
|
if (eqParts.length != 2) throw Exception("方程格式错误,应包含一个 '='。");
|
||||||
|
|
||||||
|
// Keep original equation for display
|
||||||
|
final originalEquation = _formatOriginalEquation(input);
|
||||||
|
|
||||||
|
// Parse coefficients symbolically
|
||||||
|
final leftCoeffsSymbolic = _parsePolynomialSymbolic(eqParts[0]);
|
||||||
|
final rightCoeffsSymbolic = _parsePolynomialSymbolic(eqParts[1]);
|
||||||
|
|
||||||
|
final aSymbolic = _subtractCoefficients(
|
||||||
|
leftCoeffsSymbolic[2] ?? '0',
|
||||||
|
rightCoeffsSymbolic[2] ?? '0',
|
||||||
|
);
|
||||||
|
final bSymbolic = _subtractCoefficients(
|
||||||
|
leftCoeffsSymbolic[1] ?? '0',
|
||||||
|
rightCoeffsSymbolic[1] ?? '0',
|
||||||
|
);
|
||||||
|
final cSymbolic = _subtractCoefficients(
|
||||||
|
leftCoeffsSymbolic[0] ?? '0',
|
||||||
|
rightCoeffsSymbolic[0] ?? '0',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Also get numeric values for calculations
|
||||||
final leftCoeffs = _parsePolynomial(eqParts[0]);
|
final leftCoeffs = _parsePolynomial(eqParts[0]);
|
||||||
final rightCoeffs = _parsePolynomial(eqParts[1]);
|
final rightCoeffs = _parsePolynomial(eqParts[1]);
|
||||||
|
|
||||||
final a = (leftCoeffs[2] ?? 0) - (rightCoeffs[2] ?? 0);
|
final a = (leftCoeffs[2] ?? 0) - (rightCoeffs[2] ?? 0);
|
||||||
final b = (leftCoeffs[1] ?? 0) - (rightCoeffs[1] ?? 0);
|
final b = (leftCoeffs[1] ?? 0) - (rightCoeffs[1] ?? 0);
|
||||||
final c = (leftCoeffs[0] ?? 0) - (rightCoeffs[0] ?? 0);
|
final c = (leftCoeffs[0] ?? 0) - (rightCoeffs[0] ?? 0);
|
||||||
@@ -168,8 +190,7 @@ class SolverService {
|
|||||||
stepNumber: 1,
|
stepNumber: 1,
|
||||||
title: '整理方程',
|
title: '整理方程',
|
||||||
explanation: r'将方程整理成标准形式 $ax^2+bx+c=0$。',
|
explanation: r'将方程整理成标准形式 $ax^2+bx+c=0$。',
|
||||||
formula:
|
formula: originalEquation,
|
||||||
'\$\$${a}x^2 ${b >= 0 ? '+' : ''} ${b}x ${c >= 0 ? '+' : ''} $c = 0\$\$',
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -213,36 +234,47 @@ class SolverService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final delta = b * b - 4 * a * c;
|
// Calculate delta symbolically
|
||||||
|
final deltaSymbolic = _calculateDeltaSymbolic(
|
||||||
|
aSymbolic,
|
||||||
|
bSymbolic,
|
||||||
|
cSymbolic,
|
||||||
|
);
|
||||||
|
final delta =
|
||||||
|
_rationalFromDouble(b).pow(2) -
|
||||||
|
Rational.fromInt(4) * _rationalFromDouble(a) * _rationalFromDouble(c);
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 3,
|
stepNumber: 3,
|
||||||
title: '计算判别式 (Delta)',
|
title: '计算判别式 (Delta)',
|
||||||
explanation:
|
explanation: '\$\$\\Delta = b^2 - 4ac = $deltaSymbolic\$\$',
|
||||||
'\$\$\\Delta = b^2 - 4ac = ($b)^2 - 4 \\cdot ($a) \\cdot ($c) = $delta\$\$',
|
formula:
|
||||||
formula: '\$\$\\Delta = $delta\$\$',
|
'\$\$\\Delta = $deltaSymbolic = ${delta.toDouble().toStringAsFixed(4)}\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (delta > 0) {
|
final deltaDouble = delta.toDouble();
|
||||||
final x1 = (-b + sqrt(delta)) / (2 * a);
|
if (deltaDouble > 0) {
|
||||||
final x2 = (-b - sqrt(delta)) / (2 * a);
|
// Keep sqrt symbolic instead of evaluating to decimal
|
||||||
|
final sqrtDeltaStr = _formatSqrtExpression(delta.toDouble());
|
||||||
|
final x1Expr = _formatQuadraticRoot(-b, sqrtDeltaStr, 2 * a, true);
|
||||||
|
final x2Expr = _formatQuadraticRoot(-b, sqrtDeltaStr, 2 * a, false);
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 4,
|
stepNumber: 4,
|
||||||
title: '应用求根公式',
|
title: '应用求根公式',
|
||||||
explanation:
|
explanation:
|
||||||
r'因为 $\Delta > 0$,方程有两个不相等的实数根。公式: $x = \frac{-b \pm \sqrt{\Delta}}{2a}$。',
|
r'因为 $\Delta > 0$,方程有两个不相等的实数根。公式: $x = \frac{-b \pm \sqrt{\Delta}}{2a}$。',
|
||||||
formula:
|
formula: '\$\$x_1 = $x1Expr, \\quad x_2 = $x2Expr\$\$',
|
||||||
'\$\$x_1 = ${x1.toStringAsFixed(4)}, \\quad x_2 = ${x2.toStringAsFixed(4)}\$\$',
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer:
|
finalAnswer: '\$\$x_1 = $x1Expr, \\quad x_2 = $x2Expr\$\$',
|
||||||
'\$\$x_1 = ${x1.toStringAsFixed(4)}, \\quad x_2 = ${x2.toStringAsFixed(4)}\$\$',
|
|
||||||
);
|
);
|
||||||
} else if (delta == 0) {
|
} else if (deltaDouble == 0) {
|
||||||
final x = -b / (2 * a);
|
final x = -b / (2 * a);
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
@@ -266,9 +298,10 @@ class SolverService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final sqrtDelta = sqrt(-delta);
|
// Keep sqrt symbolic for complex roots
|
||||||
|
final sqrtNegDeltaStr = _formatSqrtExpression(-delta.toDouble());
|
||||||
final realPart = -b / (2 * a);
|
final realPart = -b / (2 * a);
|
||||||
final imagPart = sqrtDelta / (2 * a);
|
final imagPartExpr = _formatImaginaryPart(sqrtNegDeltaStr, 2 * a);
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
@@ -282,7 +315,7 @@ class SolverService {
|
|||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer:
|
finalAnswer:
|
||||||
'\$\$x_1 = ${realPart.toStringAsFixed(4)} + ${imagPart.toStringAsFixed(4)}i, \\quad x_2 = ${realPart.toStringAsFixed(4)} - ${imagPart.toStringAsFixed(4)}i\$\$',
|
'\$\$x_1 = ${realPart.toStringAsFixed(4)} + $imagPartExpr, \\quad x_2 = ${realPart.toStringAsFixed(4)} - $imagPartExpr\$\$',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,16 +349,23 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2)
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final det = a1 * b2 - a2 * b1;
|
final det =
|
||||||
if (det == 0) {
|
_rationalFromDouble(a1) * _rationalFromDouble(b2) -
|
||||||
|
_rationalFromDouble(a2) * _rationalFromDouble(b1);
|
||||||
|
if (det == Rational.zero) {
|
||||||
|
final infiniteCheck =
|
||||||
|
_rationalFromDouble(a1) * _rationalFromDouble(c2) -
|
||||||
|
_rationalFromDouble(a2) * _rationalFromDouble(c1);
|
||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer: a1 * c2 - a2 * c1 == 0 ? '有无穷多解' : '无解',
|
finalAnswer: infiniteCheck == Rational.zero ? '有无穷多解' : '无解',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final newA1 = a1 * b2, newC1 = c1 * b2;
|
final newA1 = _rationalFromDouble(a1) * _rationalFromDouble(b2);
|
||||||
final newA2 = a2 * b1, newC2 = c2 * b1;
|
final newC1 = _rationalFromDouble(c1) * _rationalFromDouble(b2);
|
||||||
|
final newA2 = _rationalFromDouble(a2) * _rationalFromDouble(b1);
|
||||||
|
final newC2 = _rationalFromDouble(c2) * _rationalFromDouble(b1);
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
@@ -336,8 +376,8 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2)
|
|||||||
'''
|
'''
|
||||||
\$\$
|
\$\$
|
||||||
\\begin{cases}
|
\\begin{cases}
|
||||||
${newA1}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC1 & (3) \\\\
|
${newA1.toDouble().toStringAsFixed(2)}x ${b1 * b2 >= 0 ? '+' : ''} ${(b1 * b2).toStringAsFixed(2)}y = ${newC1.toDouble().toStringAsFixed(2)} & (3) \\\\
|
||||||
${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
${newA2.toDouble().toStringAsFixed(2)}x ${b1 * b2 >= 0 ? '+' : ''} ${(b1 * b2).toStringAsFixed(2)}y = ${newC2.toDouble().toStringAsFixed(2)} & (4)
|
||||||
\\end{cases}
|
\\end{cases}
|
||||||
\$\$
|
\$\$
|
||||||
''',
|
''',
|
||||||
@@ -353,7 +393,7 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
|||||||
title: '相减',
|
title: '相减',
|
||||||
explanation: '将方程(3)减去方程(4),得到一个只含 x 的方程。',
|
explanation: '将方程(3)减去方程(4),得到一个只含 x 的方程。',
|
||||||
formula:
|
formula:
|
||||||
'\$\$($newA1 - $newA2)x = $newC1 - $newC2 \\Rightarrow ${xCoeff}x = $constCoeff\$\$',
|
'\$\$(${newA1.toDouble().toStringAsFixed(2)} - ${newA2.toDouble().toStringAsFixed(2)})x = ${newC1.toDouble().toStringAsFixed(2)} - ${newC2.toDouble().toStringAsFixed(2)} \\Rightarrow ${xCoeff.toDouble().toStringAsFixed(2)}x = ${constCoeff.toDouble().toStringAsFixed(2)}\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -369,21 +409,21 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4)
|
|||||||
|
|
||||||
if (b1.abs() < 1e-9) {
|
if (b1.abs() < 1e-9) {
|
||||||
final yCoeff = b2;
|
final yCoeff = b2;
|
||||||
final yConst = c2 - a2 * x;
|
final yConst = c2 - a2 * x.toDouble();
|
||||||
final y = yConst / yCoeff;
|
final y = yConst / yCoeff;
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 4,
|
stepNumber: 4,
|
||||||
title: '回代求解 y',
|
title: '回代求解 y',
|
||||||
explanation: '将 x = $x 代入原方程(2)中。',
|
explanation: '将 x = ${x.toDouble().toStringAsFixed(4)} 代入原方程(2)中。',
|
||||||
formula:
|
formula:
|
||||||
'''
|
'''
|
||||||
\$\$
|
\$\$
|
||||||
\\begin{aligned}
|
\\begin{aligned}
|
||||||
$a2($x) + ${b2}y &= $c2 \\\\
|
$a2(${x.toDouble().toStringAsFixed(4)}) + ${b2}y &= $c2 \\\\
|
||||||
${a2 * x} + ${b2}y &= $c2 \\\\
|
${a2 * x.toDouble()} + ${b2}y &= $c2 \\\\
|
||||||
${b2}y &= $c2 - ${a2 * x} \\\\
|
${b2}y &= $c2 - ${a2 * x.toDouble()} \\\\
|
||||||
${b2}y &= ${c2 - a2 * x}
|
${b2}y &= ${c2 - a2 * x.toDouble()}
|
||||||
\\end{aligned}
|
\\end{aligned}
|
||||||
\$\$
|
\$\$
|
||||||
''',
|
''',
|
||||||
@@ -394,30 +434,31 @@ ${b2}y &= ${c2 - a2 * x}
|
|||||||
stepNumber: 5,
|
stepNumber: 5,
|
||||||
title: '解出 y',
|
title: '解出 y',
|
||||||
explanation: '求解得到 y 的值。',
|
explanation: '求解得到 y 的值。',
|
||||||
formula: '\$\$y = $y\$\$',
|
formula: '\$\$y = ${y.toStringAsFixed(4)}\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer: '\$\$x = $x, \\quad y = $y\$\$',
|
finalAnswer:
|
||||||
|
'\$\$x = ${x.toDouble().toStringAsFixed(4)}, \\quad y = ${y.toStringAsFixed(4)}\$\$',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final yCoeff = b1;
|
final yCoeff = b1;
|
||||||
final yConst = c1 - a1 * x;
|
final yConst = c1 - a1 * x.toDouble();
|
||||||
final y = yConst / yCoeff;
|
final y = yConst / yCoeff;
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 4,
|
stepNumber: 4,
|
||||||
title: '回代求解 y',
|
title: '回代求解 y',
|
||||||
explanation: '将 x = $x 代入原方程(1)中。',
|
explanation: '将 x = ${x.toDouble().toStringAsFixed(4)} 代入原方程(1)中。',
|
||||||
formula:
|
formula:
|
||||||
'''
|
'''
|
||||||
\$\$
|
\$\$
|
||||||
\\begin{aligned}
|
\\begin{aligned}
|
||||||
$a1($x) + ${b1}y &= $c1 \\\\
|
$a1(${x.toDouble().toStringAsFixed(4)}) + ${b1}y &= $c1 \\\\
|
||||||
${a1 * x} + ${b1}y &= $c1 \\\\
|
${a1 * x.toDouble()} + ${b1}y &= $c1 \\\\
|
||||||
${b1}y &= $c1 - ${a1 * x} \\\\
|
${b1}y &= $c1 - ${a1 * x.toDouble()} \\\\
|
||||||
${b1}y &= ${c1 - a1 * x}
|
${b1}y &= ${c1 - a1 * x.toDouble()}
|
||||||
\\end{aligned}
|
\\end{aligned}
|
||||||
\$\$
|
\$\$
|
||||||
''',
|
''',
|
||||||
@@ -428,12 +469,13 @@ ${b1}y &= ${c1 - a1 * x}
|
|||||||
stepNumber: 5,
|
stepNumber: 5,
|
||||||
title: '解出 y',
|
title: '解出 y',
|
||||||
explanation: '求解得到 y 的值。',
|
explanation: '求解得到 y 的值。',
|
||||||
formula: '\$\$y = $y\$\$',
|
formula: '\$\$y = ${y.toStringAsFixed(4)}\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return CalculationResult(
|
return CalculationResult(
|
||||||
steps: steps,
|
steps: steps,
|
||||||
finalAnswer: '\$\$x = $x, \\quad y = $y\$\$',
|
finalAnswer:
|
||||||
|
'\$\$x = ${x.toDouble().toStringAsFixed(4)}, \\quad y = ${y.toStringAsFixed(4)}\$\$',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,4 +982,331 @@ ${b1}y &= ${c1 - a1 * x}
|
|||||||
}
|
}
|
||||||
|
|
||||||
int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b);
|
int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b);
|
||||||
|
|
||||||
|
/// 格式化平方根表达式,保持符号形式
|
||||||
|
String _formatSqrtExpression(double value) {
|
||||||
|
if (value == 0) return '0';
|
||||||
|
|
||||||
|
// 处理负数(用于复数根)
|
||||||
|
if (value < 0) {
|
||||||
|
return '\\sqrt{${(-value).toInt()}}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为完全平方数
|
||||||
|
final sqrtValue = sqrt(value);
|
||||||
|
final rounded = sqrtValue.round();
|
||||||
|
if ((sqrtValue - rounded).abs() < 1e-10) {
|
||||||
|
return rounded.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 寻找最大的完全平方数因子
|
||||||
|
int maxSquareFactor = 1;
|
||||||
|
int intValue = value.toInt();
|
||||||
|
for (int i = 2; i * i <= intValue; i++) {
|
||||||
|
if (intValue % (i * i) == 0) {
|
||||||
|
maxSquareFactor = i * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final coefficient = sqrt(maxSquareFactor).round();
|
||||||
|
final remaining = intValue ~/ maxSquareFactor;
|
||||||
|
|
||||||
|
if (remaining == 1) {
|
||||||
|
return coefficient == 1
|
||||||
|
? '\\sqrt{$intValue}'
|
||||||
|
: '$coefficient\\sqrt{$remaining}';
|
||||||
|
} else if (coefficient == 1) {
|
||||||
|
return '\\sqrt{$remaining}';
|
||||||
|
} else {
|
||||||
|
return '$coefficient\\sqrt{$remaining}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 格式化二次方程的根:(-b ± sqrt(delta)) / (2a)
|
||||||
|
String _formatQuadraticRoot(
|
||||||
|
double b,
|
||||||
|
String sqrtExpr,
|
||||||
|
double denominator,
|
||||||
|
bool isPlus,
|
||||||
|
) {
|
||||||
|
final sign = isPlus ? '+' : '-';
|
||||||
|
final bStr = b == 0
|
||||||
|
? ''
|
||||||
|
: b > 0
|
||||||
|
? '${b.toInt()}'
|
||||||
|
: '(${b.toInt()})';
|
||||||
|
final denomStr = denominator == 2 ? '2' : denominator.toString();
|
||||||
|
|
||||||
|
if (b == 0) {
|
||||||
|
// 简化为 ±sqrt(delta)/denominator
|
||||||
|
if (denominator == 2) {
|
||||||
|
return isPlus
|
||||||
|
? '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}'
|
||||||
|
: '-\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}';
|
||||||
|
} else {
|
||||||
|
return isPlus
|
||||||
|
? '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}'
|
||||||
|
: '-\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 完整的表达式:(-b ± sqrt(delta))/denominator
|
||||||
|
final numerator = b > 0
|
||||||
|
? '-$bStr $sign \\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}'
|
||||||
|
: '(${b.toInt()}) $sign \\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}';
|
||||||
|
|
||||||
|
if (denominator == 2) {
|
||||||
|
return '\\frac{$numerator}{2}';
|
||||||
|
} else {
|
||||||
|
return '\\frac{$numerator}{$denomStr}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 格式化复数根的虚部:sqrt(-delta)/(2a)
|
||||||
|
String _formatImaginaryPart(String sqrtExpr, double denominator) {
|
||||||
|
final denomStr = denominator == 2 ? '2' : denominator.toString();
|
||||||
|
|
||||||
|
if (denominator == 2) {
|
||||||
|
return '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}i';
|
||||||
|
} else {
|
||||||
|
return '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}i';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 格式化原始方程,保持符号形式
|
||||||
|
String _formatOriginalEquation(String input) {
|
||||||
|
// Simply return the original equation with proper LaTeX formatting
|
||||||
|
// This avoids complex parsing issues and preserves the original symbolic form
|
||||||
|
String result = input.replaceAll(' ', '');
|
||||||
|
|
||||||
|
// 确保方程格式正确
|
||||||
|
if (!result.contains('=')) {
|
||||||
|
result = '$result=0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace sqrt with LaTeX format
|
||||||
|
result = result.replaceAll('sqrt(', '\\sqrt{');
|
||||||
|
result = result.replaceAll(')', '}');
|
||||||
|
|
||||||
|
return '\$\$$result\$\$';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析多项式,保持符号形式
|
||||||
|
Map<int, String> _parsePolynomialSymbolic(String side) {
|
||||||
|
final coeffs = <int, String>{};
|
||||||
|
|
||||||
|
// Use a simpler approach: split by terms and parse each term individually
|
||||||
|
var s = side.replaceAll(' ', ''); // Remove spaces
|
||||||
|
if (!s.startsWith('+') && !s.startsWith('-')) {
|
||||||
|
s = '+$s';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split by + and - but be more careful about parentheses and functions
|
||||||
|
final terms = <String>[];
|
||||||
|
int start = 0;
|
||||||
|
int parenDepth = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < s.length; i++) {
|
||||||
|
final char = s[i];
|
||||||
|
|
||||||
|
if (char == '(')
|
||||||
|
parenDepth++;
|
||||||
|
else if (char == ')')
|
||||||
|
parenDepth--;
|
||||||
|
|
||||||
|
// Only split on + or - when not inside parentheses
|
||||||
|
if (parenDepth == 0 && (char == '+' || char == '-') && i > start) {
|
||||||
|
terms.add(s.substring(start, i));
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terms.add(s.substring(start));
|
||||||
|
|
||||||
|
for (final term in terms) {
|
||||||
|
if (term.isEmpty) continue;
|
||||||
|
|
||||||
|
// Parse each term
|
||||||
|
final termPattern = RegExp(r'^([+-]?)(.*?)x(?:\^(\d+))?$|^([+-]?)(.*?)$');
|
||||||
|
final match = termPattern.firstMatch(term);
|
||||||
|
|
||||||
|
if (match != null) {
|
||||||
|
if (match.group(5) != null) {
|
||||||
|
// Constant term
|
||||||
|
final sign = match.group(4) ?? '+';
|
||||||
|
final value = match.group(5)!;
|
||||||
|
final coeffStr = sign == '+' && value.isNotEmpty
|
||||||
|
? value
|
||||||
|
: '$sign$value';
|
||||||
|
coeffs[0] = _combineCoefficients(coeffs[0], coeffStr);
|
||||||
|
} else {
|
||||||
|
// x term
|
||||||
|
final sign = match.group(1) ?? '+';
|
||||||
|
final coeffPart = match.group(2) ?? '';
|
||||||
|
final power = match.group(3) != null ? int.parse(match.group(3)!) : 1;
|
||||||
|
|
||||||
|
String coeffStr;
|
||||||
|
if (coeffPart.isEmpty) {
|
||||||
|
coeffStr = sign == '+' ? '1' : '-1';
|
||||||
|
} else {
|
||||||
|
coeffStr = sign == '+' ? coeffPart : '$sign$coeffPart';
|
||||||
|
}
|
||||||
|
|
||||||
|
coeffs[power] = _combineCoefficients(coeffs[power], coeffStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return coeffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 规范化系数字符串
|
||||||
|
String _normalizeCoefficientString(String coeff) {
|
||||||
|
if (coeff.isEmpty || coeff == '+') return '1';
|
||||||
|
if (coeff == '-') return '-1';
|
||||||
|
|
||||||
|
// 处理类似 "2sqrt(3)" 的情况
|
||||||
|
coeff = coeff.replaceAll(' ', ''); // 移除空格
|
||||||
|
|
||||||
|
// 检查是否是纯数字
|
||||||
|
final numValue = double.tryParse(coeff);
|
||||||
|
if (numValue != null) {
|
||||||
|
return coeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否包含 sqrt
|
||||||
|
if (coeff.contains('sqrt(') || coeff.contains('\\sqrt{')) {
|
||||||
|
// 如果前面没有数字系数,默认为 1
|
||||||
|
if (coeff.startsWith('sqrt(') || coeff.startsWith('\\sqrt{')) {
|
||||||
|
return coeff.startsWith('-') ? coeff : '1' + coeff;
|
||||||
|
}
|
||||||
|
// 如果前面有数字,保持原样
|
||||||
|
return coeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return coeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 合并系数,保持符号形式
|
||||||
|
String _combineCoefficients(String? existing, String newCoeff) {
|
||||||
|
if (existing == null || existing == '0') return newCoeff;
|
||||||
|
if (newCoeff == '0') return existing;
|
||||||
|
|
||||||
|
// 简化逻辑:如果都是数字,可以相加;否则保持原样
|
||||||
|
final existingNum = double.tryParse(existing);
|
||||||
|
final newNum = double.tryParse(newCoeff);
|
||||||
|
|
||||||
|
if (existingNum != null && newNum != null) {
|
||||||
|
final sum = existingNum + newNum;
|
||||||
|
return sum.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果包含符号表达式,直接连接
|
||||||
|
return '$existing+$newCoeff'.replaceAll('+-', '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 减去系数
|
||||||
|
String _subtractCoefficients(String a, String b) {
|
||||||
|
if (a == '0') return b.startsWith('-') ? b.substring(1) : '-$b';
|
||||||
|
if (b == '0') return a;
|
||||||
|
|
||||||
|
final aNum = double.tryParse(a);
|
||||||
|
final bNum = double.tryParse(b);
|
||||||
|
|
||||||
|
if (aNum != null && bNum != null) {
|
||||||
|
final result = aNum - bNum;
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 符号表达式相减
|
||||||
|
return '$a-${b.startsWith('-') ? b.substring(1) : b}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 计算判别式,保持符号形式
|
||||||
|
String _calculateDeltaSymbolic(String a, String b, String c) {
|
||||||
|
// Delta = b^2 - 4ac
|
||||||
|
|
||||||
|
// 计算 b^2
|
||||||
|
String bSquared;
|
||||||
|
if (b == '0') {
|
||||||
|
bSquared = '0';
|
||||||
|
} else if (b == '1') {
|
||||||
|
bSquared = '1';
|
||||||
|
} else if (b == '-1') {
|
||||||
|
bSquared = '1';
|
||||||
|
} else if (b.startsWith('-')) {
|
||||||
|
final absB = b.substring(1);
|
||||||
|
bSquared = '$absB^2';
|
||||||
|
} else {
|
||||||
|
bSquared = '$b^2';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 4ac
|
||||||
|
String fourAC;
|
||||||
|
if (a == '0' || c == '0') {
|
||||||
|
fourAC = '0';
|
||||||
|
} else {
|
||||||
|
// 处理符号
|
||||||
|
String aCoeff = a;
|
||||||
|
String cCoeff = c;
|
||||||
|
|
||||||
|
// 如果 a 或 c 是负数,需要处理符号
|
||||||
|
bool aNegative = a.startsWith('-');
|
||||||
|
bool cNegative = c.startsWith('-');
|
||||||
|
|
||||||
|
if (aNegative) aCoeff = a.substring(1);
|
||||||
|
if (cNegative) cCoeff = c.substring(1);
|
||||||
|
|
||||||
|
String acProduct;
|
||||||
|
if (aCoeff == '1' && cCoeff == '1') {
|
||||||
|
acProduct = '1';
|
||||||
|
} else if (aCoeff == '1') {
|
||||||
|
acProduct = cCoeff;
|
||||||
|
} else if (cCoeff == '1') {
|
||||||
|
acProduct = aCoeff;
|
||||||
|
} else {
|
||||||
|
acProduct = '$aCoeff \\cdot $cCoeff';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定 4ac 的符号
|
||||||
|
bool productNegative = aNegative != cNegative;
|
||||||
|
String fourACValue = '4 \\cdot $acProduct';
|
||||||
|
|
||||||
|
if (productNegative) {
|
||||||
|
fourAC = '-$fourACValue';
|
||||||
|
} else {
|
||||||
|
fourAC = fourACValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 Delta = b^2 - 4ac
|
||||||
|
if (bSquared == '0' && fourAC == '0') {
|
||||||
|
return '0';
|
||||||
|
} else if (bSquared == '0') {
|
||||||
|
return fourAC.startsWith('-') ? fourAC.substring(1) : '-$fourAC';
|
||||||
|
} else if (fourAC == '0') {
|
||||||
|
return bSquared;
|
||||||
|
} else {
|
||||||
|
String sign = fourAC.startsWith('-') ? '+' : '-';
|
||||||
|
String absFourAC = fourAC.startsWith('-') ? fourAC.substring(1) : fourAC;
|
||||||
|
return '$bSquared $sign $absFourAC';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rational _rationalFromDouble(double value, {int maxPrecision = 12}) {
|
||||||
|
// 限制小数精度,避免无限循环小数
|
||||||
|
final str = value.toStringAsFixed(maxPrecision);
|
||||||
|
|
||||||
|
if (!str.contains('.')) {
|
||||||
|
return Rational.parse(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
final parts = str.split('.');
|
||||||
|
final integerPart = parts[0];
|
||||||
|
final fractionalPart = parts[1];
|
||||||
|
|
||||||
|
final numerator = BigInt.parse(integerPart + fractionalPart);
|
||||||
|
final denominator = BigInt.from(10).pow(fractionalPart.length);
|
||||||
|
|
||||||
|
return Rational(numerator, denominator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -424,6 +424,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.5+1"
|
version: "6.1.5+1"
|
||||||
|
rational:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: rational
|
||||||
|
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@@ -39,6 +39,7 @@ dependencies:
|
|||||||
google_fonts: ^6.3.1
|
google_fonts: ^6.3.1
|
||||||
go_router: ^14.2.0
|
go_router: ^14.2.0
|
||||||
url_launcher: ^6.3.0
|
url_launcher: ^6.3.0
|
||||||
|
rational: ^2.2.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user