// tools/mixed_variance.dart import 'package:flutter/material.dart'; import 'package:rational/rational.dart'; import '../utils/math_utils.dart'; class MixedVariancePage extends StatefulWidget { @override _MixedVariancePageState createState() => _MixedVariancePageState(); } class _MixedVariancePageState extends State { final TextEditingController sampleSizesController = TextEditingController(); final TextEditingController meansController = TextEditingController(); final TextEditingController variancesController = TextEditingController(); String result = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('混合方差')), body: Padding( padding: EdgeInsets.all(16), child: Column( children: [ _buildInputField('各层样本量(用空格分隔)', sampleSizesController), _buildInputField('各层均值(用空格分隔)', meansController), _buildInputField('各层方差(用空格分隔)', variancesController), SizedBox(height: 20), ElevatedButton( onPressed: _calculate, child: Text('计算'), ), SizedBox(height: 20), if (result.isNotEmpty) Card( child: Padding( padding: EdgeInsets.all(16), child: Text( result, style: TextStyle(fontSize: 16), ), ), ), ], ), ), ); } Widget _buildInputField(String label, TextEditingController controller) { return Padding( padding: EdgeInsets.symmetric(vertical: 8), child: TextField( controller: controller, keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), decoration: InputDecoration( labelText: label, border: OutlineInputBorder(), hintText: label.contains('分隔') ? '用空格分隔多个值' : '输入分数如: 1/2 或小数', ), ), ); } void _calculate() { try { final sizes = sampleSizesController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList(); final means = meansController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList(); final variances = variancesController.text.split(' ').map((s) => MathUtils.parseRational(s)).toList(); if (sizes.length != means.length || sizes.length != variances.length) { setState(() { result = '错误: 输入的数据长度不一致'; }); return; } Rational totalSize = Rational.zero; Rational weightedSum = Rational.zero; for (int i = 0; i < sizes.length; i++) { totalSize += sizes[i]; weightedSum += sizes[i] * means[i]; } final overallMean = weightedSum / totalSize; Rational mixedVariance = Rational.zero; for (int i = 0; i < sizes.length; i++) { mixedVariance += sizes[i] * (variances[i] + (means[i] - overallMean).pow(2)) / totalSize; } // 获取近似值 double overallMeanApprox = overallMean.toDouble(); double mixedVarianceApprox = mixedVariance.toDouble(); setState(() { result = '总均值 = ${MathUtils.rationalToString(overallMean)} (≈${overallMeanApprox.toStringAsFixed(4)})\n' '混合方差 = ${MathUtils.rationalToString(mixedVariance)} (≈${mixedVarianceApprox.toStringAsFixed(4)})'; }); } catch (e) { setState(() { result = '错误: $e'; }); } } @override void dispose() { sampleSizesController.dispose(); meansController.dispose(); variancesController.dispose(); super.dispose(); } }