bisection_method.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // tools/bisection_method.dart
  2. import 'package:flutter/material.dart';
  3. import 'package:math_expressions/math_expressions.dart';
  4. class BisectionMethodPage extends StatefulWidget {
  5. @override
  6. _BisectionMethodPageState createState() => _BisectionMethodPageState();
  7. }
  8. class _BisectionMethodPageState extends State<BisectionMethodPage> {
  9. final TextEditingController _functionController = TextEditingController(text: 'x^3 - x - 2');
  10. final TextEditingController _aController = TextEditingController(text: '1');
  11. final TextEditingController _bController = TextEditingController(text: '2');
  12. final TextEditingController _toleranceController = TextEditingController(text: '0.0001');
  13. final TextEditingController _maxIterationsController = TextEditingController(text: '100');
  14. List<String> _iterations = [];
  15. String _result = '';
  16. bool _isCalculating = false;
  17. // 使用 math_expressions 库计算数学表达式
  18. double _evaluateFunction(String function, double x) {
  19. try {
  20. // 预处理函数表达式
  21. String exprString = function
  22. .replaceAll(' ', '')
  23. .replaceAll('×', '*')
  24. .replaceAll('÷', '/');
  25. // 创建解析器
  26. Parser p = Parser();
  27. // 解析表达式
  28. Expression expression = p.parse(exprString);
  29. // 创建上下文并绑定变量 x
  30. ContextModel contextModel = ContextModel()
  31. ..bindVariable(Variable('x'), Number(x));
  32. // 计算表达式
  33. double result = expression.evaluate(EvaluationType.REAL, contextModel);
  34. return result;
  35. } catch (e) {
  36. throw Exception('函数表达式解析错误: $e\n请检查表达式格式是否正确');
  37. }
  38. }
  39. void _calculate() {
  40. setState(() {
  41. _isCalculating = true;
  42. _iterations.clear();
  43. _result = '';
  44. });
  45. try {
  46. double a = double.parse(_aController.text);
  47. double b = double.parse(_bController.text);
  48. double tolerance = double.parse(_toleranceController.text);
  49. int maxIterations = int.parse(_maxIterationsController.text);
  50. String function = _functionController.text;
  51. if (a >= b) {
  52. throw Exception('区间左端点必须小于右端点');
  53. }
  54. double fa = _evaluateFunction(function, a);
  55. double fb = _evaluateFunction(function, b);
  56. if (fa.isNaN || fb.isNaN) {
  57. throw Exception('函数在端点处计算失败,请检查表达式');
  58. }
  59. if (fa * fb > 0) {
  60. throw Exception('函数在区间两端点必须异号\nf(a) = $fa, f(b) = $fb');
  61. }
  62. int iteration = 0;
  63. double c = a;
  64. while ((b - a).abs() > tolerance && iteration < maxIterations) {
  65. c = (a + b) / 2;
  66. double fc = _evaluateFunction(function, c);
  67. if (fc.isNaN) {
  68. throw Exception('迭代过程中函数计算失败');
  69. }
  70. _iterations.add('迭代 ${iteration + 1}: a=${a.toStringAsFixed(6)}, b=${b.toStringAsFixed(6)}, c=${c.toStringAsFixed(6)}, f(c)=${fc.toStringAsFixed(6)}');
  71. if (fc.abs() < tolerance) {
  72. break;
  73. } else if (fa * fc < 0) {
  74. b = c;
  75. fb = fc;
  76. } else {
  77. a = c;
  78. fa = fc;
  79. }
  80. iteration++;
  81. }
  82. setState(() {
  83. _result = '近似解: ${c.toStringAsFixed(8)}\n'
  84. '迭代次数: $iteration\n'
  85. '最终误差: ${(b - a).abs().toStringAsFixed(8)}';
  86. });
  87. } catch (e) {
  88. setState(() {
  89. _result = '错误: $e';
  90. });
  91. } finally {
  92. setState(() {
  93. _isCalculating = false;
  94. });
  95. }
  96. }
  97. void _clear() {
  98. setState(() {
  99. _functionController.text = 'x^3 - x - 2';
  100. _aController.text = '1';
  101. _bController.text = '2';
  102. _toleranceController.text = '0.0001';
  103. _maxIterationsController.text = '100';
  104. _iterations.clear();
  105. _result = '';
  106. });
  107. }
  108. @override
  109. Widget build(BuildContext context) {
  110. return Scaffold(
  111. appBar: AppBar(
  112. title: Text('二分法求根', style: TextStyle(color: Colors.black)),
  113. backgroundColor: Colors.white,
  114. elevation: 1,
  115. iconTheme: IconThemeData(color: Colors.black),
  116. ),
  117. body: SingleChildScrollView(
  118. padding: EdgeInsets.all(16),
  119. child: Column(
  120. crossAxisAlignment: CrossAxisAlignment.start,
  121. children: [
  122. Text(
  123. '二分法求方程近似解',
  124. style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.deepPurple),
  125. ),
  126. SizedBox(height: 16),
  127. Text(
  128. '使用方法:\n'
  129. '1. 输入函数表达式(支持: +, -, *, /, ^, sin, cos, tan, ln, e, sqrt 等)\n'
  130. '2. 输入有根区间 [a, b]\n'
  131. '3. 设置精度和最大迭代次数\n'
  132. '4. 点击计算获取近似解\n\n'
  133. '示例表达式:\n'
  134. '• x^3 - x - 2\n'
  135. '• sin(x) + cos(x)\n'
  136. '• e^x - 2\n'
  137. '• ln(x) + sqrt(x)',
  138. style: TextStyle(fontSize: 14, color: Colors.grey[600]),
  139. ),
  140. SizedBox(height: 20),
  141. // 函数输入
  142. _buildInputField('函数 f(x)', '例如: x^3 - x - 2', _functionController),
  143. SizedBox(height: 16),
  144. Row(
  145. children: [
  146. Expanded(child: _buildInputField('区间左端点 a', '1', _aController)),
  147. SizedBox(width: 16),
  148. Expanded(child: _buildInputField('区间右端点 b', '2', _bController)),
  149. ],
  150. ),
  151. SizedBox(height: 16),
  152. Row(
  153. children: [
  154. Expanded(child: _buildInputField('精度 tolerance', '0.0001', _toleranceController)),
  155. SizedBox(width: 16),
  156. Expanded(child: _buildInputField('最大迭代次数', '100', _maxIterationsController)),
  157. ],
  158. ),
  159. SizedBox(height: 24),
  160. // 按钮
  161. Row(
  162. children: [
  163. Expanded(
  164. child: ElevatedButton(
  165. onPressed: _isCalculating ? null : _calculate,
  166. style: ElevatedButton.styleFrom(
  167. backgroundColor: Colors.deepPurple,
  168. padding: EdgeInsets.symmetric(vertical: 16),
  169. ),
  170. child: _isCalculating
  171. ? SizedBox(height: 20, width: 20, child: CircularProgressIndicator(color: Colors.white))
  172. : Text('计算', style: TextStyle(fontSize: 16, color: Colors.white)),
  173. ),
  174. ),
  175. SizedBox(width: 16),
  176. Expanded(
  177. child: OutlinedButton(
  178. onPressed: _clear,
  179. style: OutlinedButton.styleFrom(
  180. padding: EdgeInsets.symmetric(vertical: 16),
  181. ),
  182. child: Text('清空', style: TextStyle(fontSize: 16)),
  183. ),
  184. ),
  185. ],
  186. ),
  187. SizedBox(height: 24),
  188. // 结果展示
  189. if (_result.isNotEmpty) ...[
  190. Container(
  191. width: double.infinity,
  192. padding: EdgeInsets.all(16),
  193. decoration: BoxDecoration(
  194. color: _result.contains('错误') ? Colors.red[50] : Colors.green[50],
  195. borderRadius: BorderRadius.circular(8),
  196. border: Border.all(color: _result.contains('错误') ? Colors.red : Colors.green),
  197. ),
  198. child: Text(
  199. _result,
  200. style: TextStyle(
  201. fontSize: 16,
  202. fontWeight: FontWeight.bold,
  203. color: _result.contains('错误') ? Colors.red[800] : Colors.green[800]
  204. ),
  205. ),
  206. ),
  207. SizedBox(height: 16),
  208. ],
  209. // 迭代过程
  210. if (_iterations.isNotEmpty) ...[
  211. Text('迭代过程:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
  212. SizedBox(height: 8),
  213. Container(
  214. height: 200,
  215. decoration: BoxDecoration(
  216. border: Border.all(color: Colors.grey[300]!),
  217. borderRadius: BorderRadius.circular(8),
  218. ),
  219. child: ListView.builder(
  220. itemCount: _iterations.length,
  221. itemBuilder: (context, index) {
  222. return Container(
  223. padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
  224. decoration: BoxDecoration(
  225. border: index > 0 ? Border(top: BorderSide(color: Colors.grey[200]!)) : null,
  226. color: index % 2 == 0 ? Colors.grey[50] : Colors.white,
  227. ),
  228. child: Text(
  229. _iterations[index],
  230. style: TextStyle(fontSize: 12, fontFamily: 'Monospace'),
  231. ),
  232. );
  233. },
  234. ),
  235. ),
  236. ],
  237. ],
  238. ),
  239. ),
  240. );
  241. }
  242. Widget _buildInputField(String label, String hint, TextEditingController controller) {
  243. return Column(
  244. crossAxisAlignment: CrossAxisAlignment.start,
  245. children: [
  246. Text(label, style: TextStyle(fontWeight: FontWeight.bold)),
  247. SizedBox(height: 4),
  248. TextField(
  249. controller: controller,
  250. decoration: InputDecoration(
  251. hintText: hint,
  252. border: OutlineInputBorder(),
  253. contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
  254. ),
  255. ),
  256. ],
  257. );
  258. }
  259. }