Compare commits

..

2 Commits

Author SHA1 Message Date
9339a876fa 🗑️ Clean up code 2025-09-16 01:29:49 +08:00
2f8bb4e1a0 💄 Optimize path to solve 2025-09-16 01:29:18 +08:00
2 changed files with 72 additions and 133 deletions

View File

@@ -225,42 +225,11 @@ class SolverService {
), ),
); );
if (a == a.round() && b == b.round() && c == c.round()) {
final factors = _tryFactorization(a.toInt(), b.toInt(), c.toInt());
if (factors != null) {
steps.add(
CalculationStep(
stepNumber: 2,
title: '因式分解法 (十字相乘)',
explanation: '我们发现可以将方程分解为两个一次因式的乘积。',
formula: factors.formula,
),
);
steps.add(
CalculationStep(
stepNumber: 3,
title: '求解',
explanation: '分别令每个因式等于 0解出 x。',
formula: factors.solution,
),
);
steps.add(
CalculationStep(
stepNumber: 4,
title: '化简结果',
explanation: '将分数化简到最简形式,并将负号写在分数外面。',
formula: factors.solution,
),
);
return CalculationResult(steps: steps, finalAnswer: factors.solution);
}
}
steps.add( steps.add(
CalculationStep( CalculationStep(
stepNumber: 2, stepNumber: 2,
title: '选择解法', title: '选择解法',
explanation: '无法进行因式分解,我们选择使用配方法。', explanation: '我们选择使用配方法。',
formula: r'配方法:$x^2 + \frac{b}{a}x + \frac{c}{a} = 0$', formula: r'配方法:$x^2 + \frac{b}{a}x + \frac{c}{a} = 0$',
), ),
); );
@@ -281,7 +250,7 @@ class SolverService {
stepNumber: 3, stepNumber: 3,
title: '方程变形', title: '方程变形',
explanation: a == 1 ? '方程已经是标准形式。' : '将方程两边同时除以 $a', explanation: a == 1 ? '方程已经是标准形式。' : '将方程两边同时除以 $a',
formula: '\$\$${currentEquation}\$\$', formula: '\$\$$currentEquation\$\$',
), ),
); );
@@ -301,19 +270,19 @@ class SolverService {
final halfCoeff = b / (2 * a); final halfCoeff = b / (2 * a);
final completeSquareTerm = halfCoeff * halfCoeff; final completeSquareTerm = halfCoeff * halfCoeff;
final completeStr = completeSquareTerm >= 0 final completeStr = completeSquareTerm >= 0
? '+${completeSquareTerm}' ? '+$completeSquareTerm'
: completeSquareTerm.toString(); : completeSquareTerm.toString();
final xTerm = halfCoeff >= 0 ? "+${halfCoeff}" : halfCoeff.toString(); final xTerm = halfCoeff >= 0 ? "+$halfCoeff" : halfCoeff.toString();
final rightSide = "${-constantTerm} ${completeStr}"; final rightSide = "${-constantTerm} $completeStr";
steps.add( steps.add(
CalculationStep( CalculationStep(
stepNumber: 5, stepNumber: 5,
title: '配方', title: '配方',
explanation: explanation:
'在方程两边同时加上 \$(\\frac{b}{2a})^2 = ${completeSquareTerm}\$ 以配成完全平方。', '在方程两边同时加上 \$(\\frac{b}{2a})^2 = $completeSquareTerm\$ 以配成完全平方。',
formula: '\$\$(x ${xTerm})^2 = $rightSide\$\$', formula: '\$\$(x $xTerm)^2 = $rightSide\$\$',
), ),
); );
@@ -321,7 +290,7 @@ class SolverService {
final rightSideValue = -constantTerm + completeSquareTerm; final rightSideValue = -constantTerm + completeSquareTerm;
final rightSideStrValue = rightSideValue >= 0 final rightSideStrValue = rightSideValue >= 0
? rightSideValue.toString() ? rightSideValue.toString()
: '(${rightSideValue})'; : '($rightSideValue)';
steps.add( steps.add(
CalculationStep( CalculationStep(
@@ -329,7 +298,7 @@ class SolverService {
title: '化简', title: '化简',
explanation: '合并右边的常数项。', explanation: '合并右边的常数项。',
formula: formula:
'\$\$(x ${halfCoeff >= 0 ? "+" : ""}${halfCoeff})^2 = $rightSideStrValue\$\$', '\$\$(x ${halfCoeff >= 0 ? "+" : ""}$halfCoeff)^2 = $rightSideStrValue\$\$',
), ),
); );
@@ -345,13 +314,14 @@ class SolverService {
title: '开方', title: '开方',
explanation: '对方程两边同时开平方。', explanation: '对方程两边同时开平方。',
formula: formula:
'\$\$x ${halfCoeff >= 0 ? "+" : ""}${halfCoeff} = \\pm $sqrtStr\$\$', '\$\$x ${halfCoeff >= 0 ? "+" : ""}$halfCoeff = \\pm $sqrtStr\$\$',
), ),
); );
// Step 6: Solve for x - use symbolic forms when possible // Step 6: Solve for x - use symbolic forms when possible
final discriminant = b * b - 4 * a * c;
if (rightSideValue >= 0) { if (rightSideValue >= 0) {
final roots = _calculateSymbolicRoots(a, b, rightSideValue, symbolicSqrt); final roots = _calculateSymbolicRoots(a, b, discriminant, symbolicSqrt);
steps.add( steps.add(
CalculationStep( CalculationStep(
@@ -365,7 +335,7 @@ class SolverService {
return CalculationResult(steps: steps, finalAnswer: roots.finalAnswer); return CalculationResult(steps: steps, finalAnswer: roots.finalAnswer);
} else { } else {
// Complex roots // Complex roots
final imagPart = sqrt(rightSideValue.abs()); final imagPart = sqrt(-discriminant) / (2 * a);
steps.add( steps.add(
CalculationStep( CalculationStep(
stepNumber: 8, stepNumber: 8,
@@ -920,42 +890,6 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
return [a, b, c]; return [a, b, c];
} }
({String formula, String solution})? _tryFactorization(int a, int b, int c) {
if (a == 0) return null;
int ac = a * c;
int absAc = ac.abs();
// Try all divisors of abs(ac) and consider both positive and negative factors
for (int d = 1; d <= sqrt(absAc).toInt(); d++) {
if (absAc % d == 0) {
int d1 = d;
int d2 = absAc ~/ d;
// Try all sign combinations for the factors
// We need m * n = ac and m + n = b
List<int> signCombinations = [1, -1];
for (int sign1 in signCombinations) {
for (int sign2 in signCombinations) {
int m = sign1 * d1;
int n = sign2 * d2;
if (m + n == b && m * n == ac) {
return formatFactor(m, n, a);
}
// Also try the swapped version
m = sign1 * d2;
n = sign2 * d1;
if (m + n == b && m * n == ac) {
return formatFactor(m, n, a);
}
}
}
}
}
return null;
}
bool check(int m, int n, int b) => m + n == b; bool check(int m, int n, int b) => m + n == b;
({String formula, String solution}) formatFactor(int m, int n, int a) { ({String formula, String solution}) formatFactor(int m, int n, int a) {
@@ -1278,16 +1212,30 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
formula = '\$\$x_1 = $root1Expr, \\quad x_2 = $root2Expr\$\$'; formula = '\$\$x_1 = $root1Expr, \\quad x_2 = $root2Expr\$\$';
finalAnswer = '\$\$x_1 = $root1Expr, \\quad x_2 = $root2Expr\$\$'; finalAnswer = '\$\$x_1 = $root1Expr, \\quad x_2 = $root2Expr\$\$';
} else {
// 尝试使用有理数计算精确根
final aRat = _rationalFromDouble(a);
final bRat = _rationalFromDouble(b);
final discriminantRat = _rationalFromDouble(discriminant);
final halfCoeffRat = bRat / (Rational(BigInt.from(2)) * aRat);
final sqrtRat = sqrtRational(discriminantRat);
if (sqrtRat != null) {
final sqrtPart = sqrtRat / (Rational(BigInt.from(2)) * aRat);
final x1Rat = -halfCoeffRat + sqrtPart;
final x2Rat = -halfCoeffRat - sqrtPart;
final x1Str = _formatRational(x1Rat);
final x2Str = _formatRational(x2Rat);
formula = '\$\$x_1 = $x1Str, \\quad x_2 = $x2Str\$\$';
finalAnswer = '\$\$x_1 = $x1Str, \\quad x_2 = $x2Str\$\$';
} else { } else {
// 回退到数值计算 // 回退到数值计算
final sqrtValue = sqrt(discriminant); final sqrtValue = sqrt(discriminant);
final x1 = -halfCoeff + sqrtValue; final x1 = -halfCoeff + sqrtValue / (2 * a);
final x2 = -halfCoeff - sqrtValue; final x2 = -halfCoeff - sqrtValue / (2 * a);
formula = '\$\$x_1 = $x1, \\quad x_2 = $x2\$\$';
formula =
'\$\$x_1 = ${-halfCoeff} + $sqrtValue = $x1, \\quad x_2 = ${-halfCoeff} - $sqrtValue = $x2\$\$';
finalAnswer = '\$\$x_1 = $x1, \\quad x_2 = $x2\$\$'; finalAnswer = '\$\$x_1 = $x1, \\quad x_2 = $x2\$\$';
} }
}
return (formula: formula, finalAnswer: finalAnswer); return (formula: formula, finalAnswer: finalAnswer);
} }
@@ -1361,46 +1309,27 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
} }
} }
/// 测试方法:验证修复效果 /// 检查有理数是否为完全平方数,如果是则返回其平方根
void testParenthesesFix() { Rational? sqrtRational(Rational r) {
print('=== 测试括号修复效果 ==='); if (r < Rational.zero) return null;
// 测试案例1: 已经标准化的方程 final n = r.numerator;
final test1 = 'x^2+4x-8=0'; final d = r.denominator;
print('测试输入: $test1');
final result1 = solve(test1);
print('整理方程步骤:');
result1.steps.forEach((step) {
if (step.title == '整理方程') {
print(' 公式: ${step.formula}');
}
});
print('预期: x^2+4x-8=0 (无括号)');
print('');
// 测试案例2: 需要展开的方程 final sqrtN = sqrt(n.toDouble()).round();
final test2 = '(x+2)^2=x^2+4x+4'; if (BigInt.from(sqrtN) * BigInt.from(sqrtN) == n) {
print('测试输入: $test2'); final sqrtD = sqrt(d.toDouble()).round();
final result2 = solve(test2); if (BigInt.from(sqrtD) * BigInt.from(sqrtD) == d) {
print('整理方程步骤:'); return Rational(BigInt.from(sqrtN), BigInt.from(sqrtD));
result2.steps.forEach((step) { }
if (step.title == '整理方程') {
print(' 公式: ${step.formula}');
} }
});
print('预期: 展开后无不必要的括号');
print('');
// 测试案例3: 因式分解 return null;
final test3 = '(x+1)(x-1)=x^2-1';
print('测试输入: $test3');
final result3 = solve(test3);
print('整理方程步骤:');
result3.steps.forEach((step) {
if (step.title == '整理方程') {
print(' 公式: ${step.formula}');
} }
});
print('预期: 展开后无不必要的括号'); /// 格式化有理数为 LaTeX 分数形式
String _formatRational(Rational r) {
if (r.denominator == BigInt.one) return r.numerator.toString();
return '\\frac{${r.numerator}}{${r.denominator}}';
} }
} }

View File

@@ -20,8 +20,7 @@ void main() {
final result = solver.solve('x^2 - 5x + 6 = 0'); final result = solver.solve('x^2 - 5x + 6 = 0');
debugPrint(result.finalAnswer); debugPrint(result.finalAnswer);
expect( expect(
result.finalAnswer.contains('x_1 = 2') && result.finalAnswer.contains('3') && result.finalAnswer.contains('2'),
result.finalAnswer.contains('x_2 = 3'),
true, true,
); );
}); });
@@ -58,15 +57,13 @@ void main() {
test('二次方程根的简化', () { test('二次方程根的简化', () {
final result = solver.solve('x^2 - 4x - 5 = 0'); final result = solver.solve('x^2 - 4x - 5 = 0');
debugPrint('Result for x^2 - 4x - 5 = 0: ${result.finalAnswer}'); debugPrint('Result for x^2 - 4x - 5 = 0: ${result.finalAnswer}');
// 这个方程的根应该是 x = (4 ± √(16 + 20))/2 = (4 ± √36)/2 = (4 ± 6)/2 // 这个方程的根应该是 x = (4 ± √36)/2 = (4 ± 6)/2
// 所以 x1 = (4 + 6)/2 = 5, x2 = (4 - 6)/2 = -1 // 所以 x1 = 5, x2 = -1
expect( expect(
(result.finalAnswer.contains('x_1 = 5') && result.finalAnswer.contains('2 + 3') &&
result.finalAnswer.contains('x_2 = -1')) || result.finalAnswer.contains('2 - 3'),
(result.finalAnswer.contains('x_1 = -1') &&
result.finalAnswer.contains('x_2 = 5')),
true, true,
reason: '方程 x^2 - 4x - 5 = 0 的根应该被正确简化', reason: '方程 x^2 - 4x - 5 = 0 的根应该被表示为 2 ± 3',
); );
}); });
@@ -157,5 +154,18 @@ void main() {
reason: '结果应该包含 x = -2 ± 2√3 的形式', reason: '结果应该包含 x = -2 ± 2√3 的形式',
); );
}); });
test('解 9(x-3)^2=16', () {
final result = solver.solve('9(x-3)^2=16');
debugPrint('Result for 9(x-3)^2=16: ${result.finalAnswer}');
// 验证结果包含正确的根
expect(
result.finalAnswer.contains('\\frac{5}{3}') &&
result.finalAnswer.contains('\\frac{13}{3}'),
true,
reason: '方程 9(x-3)^2=16 的根应该是 x = 5/3 和 x = 13/3',
);
});
}); });
} }