mixed_variance.dart 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // tools/mixed_variance.dart
  2. import 'package:flutter/material.dart';
  3. import 'package:rational/rational.dart';
  4. import '../utils/math_utils.dart';
  5. class MixedVariancePage extends StatefulWidget {
  6. @override
  7. _MixedVariancePageState createState() => _MixedVariancePageState();
  8. }
  9. class _MixedVariancePageState extends State<MixedVariancePage> {
  10. final TextEditingController sampleSizesController = TextEditingController();
  11. final TextEditingController meansController = TextEditingController();
  12. final TextEditingController variancesController = TextEditingController();
  13. String result = '';
  14. @override
  15. Widget build(BuildContext context) {
  16. return Scaffold(
  17. appBar: AppBar(title: Text('混合方差')),
  18. body: Padding(
  19. padding: EdgeInsets.all(16),
  20. child: Column(
  21. children: [
  22. _buildInputField('各层样本量(用空格分隔)', sampleSizesController),
  23. _buildInputField('各层均值(用空格分隔)', meansController),
  24. _buildInputField('各层方差(用空格分隔)', variancesController),
  25. SizedBox(height: 20),
  26. ElevatedButton(
  27. onPressed: _calculate,
  28. child: Text('计算'),
  29. ),
  30. SizedBox(height: 20),
  31. if (result.isNotEmpty)
  32. Card(
  33. child: Padding(
  34. padding: EdgeInsets.all(16),
  35. child: Text(
  36. result,
  37. style: TextStyle(fontSize: 16),
  38. ),
  39. ),
  40. ),
  41. ],
  42. ),
  43. ),
  44. );
  45. }
  46. Widget _buildInputField(String label, TextEditingController controller) {
  47. return Padding(
  48. padding: EdgeInsets.symmetric(vertical: 8),
  49. child: TextField(
  50. controller: controller,
  51. keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
  52. decoration: InputDecoration(
  53. labelText: label,
  54. border: OutlineInputBorder(),
  55. hintText: label.contains('分隔') ? '用空格分隔多个值' : '输入分数如: 1/2 或小数',
  56. ),
  57. ),
  58. );
  59. }
  60. void _calculate() {
  61. try {
  62. final sizes = sampleSizesController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList();
  63. final means = meansController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList();
  64. final variances = variancesController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList();
  65. if (sizes.length != means.length || sizes.length != variances.length) {
  66. setState(() {
  67. result = '错误: 输入的数据长度不一致';
  68. });
  69. return;
  70. }
  71. Rational totalSize = Rational.zero;
  72. Rational weightedSum = Rational.zero;
  73. for (int i = 0; i < sizes.length; i++) {
  74. totalSize += sizes[i];
  75. weightedSum += sizes[i] * means[i];
  76. }
  77. final overallMean = weightedSum / totalSize;
  78. Rational mixedVariance = Rational.zero;
  79. for (int i = 0; i < sizes.length; i++) {
  80. mixedVariance += sizes[i] * (variances[i] + (means[i] - overallMean).pow(2)) / totalSize;
  81. }
  82. // 获取近似值
  83. double overallMeanApprox = overallMean.toDouble();
  84. double mixedVarianceApprox = mixedVariance.toDouble();
  85. setState(() {
  86. result = '总均值 = ${MathUtils.rationalToString(overallMean)} (≈${overallMeanApprox.toStringAsFixed(4)})\n'
  87. '混合方差 = ${MathUtils.rationalToString(mixedVariance)} (≈${mixedVarianceApprox.toStringAsFixed(4)})';
  88. });
  89. } catch (e) {
  90. setState(() {
  91. result = '错误: $e';
  92. });
  93. }
  94. }
  95. @override
  96. void dispose() {
  97. sampleSizesController.dispose();
  98. meansController.dispose();
  99. variancesController.dispose();
  100. super.dispose();
  101. }
  102. }