| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- // tools/binomial_distribution.dart
- import 'package:flutter/material.dart';
- import 'package:rational/rational.dart';
- import 'package:fl_chart/fl_chart.dart';
- import '../utils/math_utils.dart';
- class BinomialDistributionPage extends StatefulWidget {
- @override
- _BinomialDistributionPageState createState() => _BinomialDistributionPageState();
- }
- class _BinomialDistributionPageState extends State<BinomialDistributionPage> {
- final TextEditingController nController = TextEditingController();
- final TextEditingController pController = TextEditingController();
- String expectation = '';
- String variance = '';
- List<String> probabilities = [];
- List<BarChartGroupData> barGroups = [];
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text('二项分布')),
- body: Padding(
- padding: EdgeInsets.all(16),
- child: SingleChildScrollView( // 添加滚动容器
- child: Column(
- children: [
- _buildInputField('n (试验次数)', nController),
- _buildInputField('p (成功概率)', pController),
- SizedBox(height: 20),
- ElevatedButton(
- onPressed: _calculate,
- child: Text('计算'),
- ),
- SizedBox(height: 20),
- if (expectation.isNotEmpty) ...[
- _buildResultCard('期望', expectation),
- _buildResultCard('方差', variance),
- SizedBox(height: 10),
- Text('概率分布:', style: TextStyle(fontWeight: FontWeight.bold)),
- if (barGroups.isNotEmpty) ...[
- SizedBox(height: 10),
- Container(
- height: 300,
- child: BarChart(
- BarChartData(
- barTouchData: BarTouchData(
- enabled: false, // 禁用触摸交互
- ),
- titlesData: FlTitlesData(
- show: true,
- bottomTitles: AxisTitles(
- sideTitles: SideTitles(
- showTitles: true,
- getTitlesWidget: (value, meta) {
- return Padding(
- padding: const EdgeInsets.only(top: 8.0),
- child: Text(
- value.toInt().toString(),
- style: TextStyle(fontSize: 10),
- ),
- );
- },
- reservedSize: 30,
- ),
- ),
- leftTitles: AxisTitles(
- sideTitles: SideTitles(
- showTitles: true,
- getTitlesWidget: (value, meta) {
- if (value % 0.1 == 0) {
- return Text(
- value.toStringAsFixed(1),
- style: TextStyle(fontSize: 10),
- );
- }
- return Text('');
- },
- reservedSize: 40,
- ),
- ),
- topTitles: AxisTitles(
- sideTitles: SideTitles(showTitles: false),
- ),
- rightTitles: AxisTitles(
- sideTitles: SideTitles(showTitles: false),
- ),
- ),
- borderData: FlBorderData(show: true),
- barGroups: barGroups,
- gridData: FlGridData(show: true),
- alignment: BarChartAlignment.spaceAround,
- maxY: _getMaxY(),
- ),
- ),
- ),
- SizedBox(height: 10),
- ],
- // 移除Expanded并替换为固定高度的ListView
- Container(
- height: 200, // 设置固定高度
- child: ListView.builder(
- itemCount: probabilities.length,
- itemBuilder: (context, index) {
- return ListTile(
- title: Text(probabilities[index]),
- );
- },
- ),
- ),
- ],
- ],
- ),
- ),
- ),
- );
- }
- double _getMaxY() {
- if (barGroups.isEmpty) return 1.0;
- double maxY = 0.0;
- for (var group in barGroups) {
- for (var rod in group.barRods) {
- if (rod.toY > maxY) maxY = rod.toY;
- }
- }
- return maxY * 1.1;
- }
- 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(),
- ),
- ),
- );
- }
- Widget _buildResultCard(String title, String value) {
- return Card(
- child: ListTile(
- title: Text(title),
- subtitle: Text(value),
- ),
- );
- }
- Rational _combination(int n, int k) {
- if (k < 0 || k > n) return Rational.zero;
- if (k == 0 || k == n) return Rational.one;
- BigInt result = BigInt.one;
- for (int i = 1; i <= k; i++) {
- result = result * BigInt.from(n - i + 1) ~/ BigInt.from(i);
- }
- return Rational(result);
- }
- void _calculate() {
- try {
- final n = int.parse(nController.text);
- final p = MathUtils.parseRational(pController.text);
- final EX = MathUtils.fromInt(n) * p;
- final DX = MathUtils.fromInt(n) * p * (Rational.one - p);
- final probs = <String>[];
- final barGroupsData = <BarChartGroupData>[];
- // 首先计算所有概率并找到最大值
- List<double> probValues = [];
- double maxProb = 0.0;
-
- for (int i = 0; i <= n; i++) {
- final comb = _combination(n, i);
- final prob = comb * p.pow(i) * (Rational.one - p).pow(n - i);
- final probDouble = prob.toDouble();
-
- probValues.add(probDouble);
- if (probDouble > maxProb) {
- maxProb = probDouble;
- }
-
- probs.add('P(X = $i) = ${MathUtils.rationalToString(prob)}');
- }
- // 找到所有具有最大概率的i值
- List<int> maxProbIndices = [];
- for (int i = 0; i < probValues.length; i++) {
- if (probValues[i] == maxProb) {
- maxProbIndices.add(i);
- }
- }
- // 确定最高组的范围
- int minMaxIndex = maxProbIndices.reduce((a, b) => a < b ? a : b);
- int maxMaxIndex = maxProbIndices.reduce((a, b) => a > b ? a : b);
- // 构建柱状图数据
- for (int i = 0; i <= n; i++) {
- double probDouble = probValues[i];
-
- barGroupsData.add(
- BarChartGroupData(
- x: i,
- barRods: [
- BarChartRodData(
- toY: probDouble,
- color: _getBarColor(i, minMaxIndex, maxMaxIndex),
- width: 16,
- borderRadius: BorderRadius.circular(4),
- ),
- ],
- ),
- );
- }
- setState(() {
- expectation = 'EX = ${MathUtils.rationalToString(EX)}';
- variance = 'DX = ${MathUtils.rationalToString(DX)}';
- probabilities = probs;
- barGroups = barGroupsData;
- });
- } catch (e) {
- setState(() {
- expectation = '错误: $e';
- variance = '';
- probabilities = [];
- barGroups = [];
- });
- }
- }
- Color _getBarColor(int x, int minMaxIndex, int maxMaxIndex) {
- if (x >= minMaxIndex && x <= maxMaxIndex) {
- // 最高组(可能有多组)
- return Colors.red;
- } else if (x < minMaxIndex) {
- // 最高组左边的组
- return Colors.blue.shade300;
- } else {
- // 最高组右边的组
- return Colors.green.shade300;
- }
- }
- @override
- void dispose() {
- nController.dispose();
- pController.dispose();
- super.dispose();
- }
- }
|