使用 JavaFX + Reactor 绘制函数图像并数值求解交点 —— 原理与实现

发布时间:2026/9/22 2:13:27

使用 JavaFX + Reactor 绘制函数图像并数值求解交点 —— 原理与实现 使用 JavaFX Reactor 绘制函数图像并数值求解交点 —— 原理与实现在数值计算与可视化中我们常常需要绘制函数曲线并标注其交点。本文将以一个完整的 Java 程序为例深入剖析如何利用Reactor生成数据点、JavaFX绘制曲线并通过纯数值方法符号检测 二分法求解交点最终在图上以空心圆圈和坐标标签的形式清晰显示。代码全程不依赖任何数学求导公式完全基于离散点采样与迭代逼近具有通用性强、易于扩展的特点。一、程序概览我们定义两个函数( f1(x) 3x^2 - 1 )二次函数( f2(x) 2x - 1 )一次函数目标是绘制它们在区间 ([-5, 5]) 上的图像并求出所有交点坐标在图上用空心圆标注。二、数据生成Reactor 的Flux流2.1 为何使用 ReactorReactor 提供了一套响应式编程 API其Flux可以方便地生成大量离散数据点并以声明式的方式进行处理。与传统 for 循环相比Flux更易于组合、并行处理和链式操作。2.2 代码解析doublestep0.01;doublestartX-5.0;doubleendX5.0;intcount(int)((endX-startX)/step)1;Flux.range(0,count).map(i-startXi*step).doOnNext(x-{series1.getData().add(newXYChart.Data(x,f1(x)));series2.getData().add(newXYChart.Data(x,f2(x)));}).blockLast();Flux.range(0, count)生成一个整数序列 0 到 count-1。map(i - startX i * step)将每个索引映射为对应的 ( x ) 值从而得到均匀分布的离散点。doOnNext是副作用操作在每次发出 ( x ) 时计算两个函数值并添加到 JavaFX 的Series数据列表中。blockLast()阻塞当前线程直到所有数据生成完毕。由于数据量不大约 1000 点该操作对 UI 线程影响很小。这样我们便得到了绘制曲线所需的所有点。三、绘图JavaFX 的LineChart3.1 坐标轴设置NumberAxisxAxisnewNumberAxis();xAxis.setLowerBound(-5);xAxis.setUpperBound(5);xAxis.setAutoRanging(false);固定 x 轴范围确保定义域完整显示。y 轴则自动适应数据范围。3.2 创建图表与系列LineChartNumber,NumberchartnewLineChart(xAxis,yAxis);XYChart.SeriesNumber,Numberseries1newXYChart.Series();series1.setName(f1(x) 3x² - 1);LineChart会按照数据点顺序连接成折线由于步长足够小0.01曲线将非常平滑。setCreateSymbols(false)禁用默认数据点符号避免干扰。3.3 添加数据在doOnNext中我们直接将计算出的数据点加入series的getData().add()该方法会在 JavaFX 应用程序线程中更新但blockLast()会阻塞 UI 线程直到所有点添加完毕这个过程只持续几十毫秒用户无感知。四、数值求解交点4.1 原理两个连续函数的交点满足 ( f_1(x) f_2(x) )等价于差值函数 ( h(x) f_1(x) - f_2(x) ) 的零点。若 ( h(x) ) 在某区间两端异号则根据介值定理该区间内必存在至少一个根。4.2 实现步骤离散采样在定义域内以步长step采样计算每个点的 ( h(x) )。符号检测遍历相邻点若h1 * h2 0则说明该区间内存在根。二分法精化对每个含根区间使用二分法Bisection Method迭代求根直到区间宽度小于给定精度1e-10。处理端点若某点 ( h(x) ) 恰好为零或绝对值极小直接视为交点。privateListdouble[]findIntersections(doublestep,doublestartX,doubleendX){Listdouble[]resultnewArrayList();intcount(int)((endX-startX)/step)1;double[]xValsnewdouble[count];double[]hValsnewdouble[count];for(inti0;icount;i){doublexstartXi*step;xVals[i]x;hVals[i]f1(x)-f2(x);}for(inti0;icount-1;i){if(hVals[i]*hVals[i1]0){doublerootbisect(xVals[i],xVals[i1]);doubleyf1(root);result.add(newdouble[]{root,y});}if(Math.abs(hVals[i])1e-12){doubleyf1(xVals[i]);result.add(newdouble[]{xVals[i],y});}}// 检查最后一点if(Math.abs(hVals[count-1])1e-12){doubleyf1(xVals[count-1]);result.add(newdouble[]{xVals[count-1],y});}returnresult;}4.3 二分法细节privatedoublebisect(doublea,doubleb){doubleeps1e-10;doublemid;intmaxIter100;for(inti0;imaxIter;i){mid(ab)/2;doublefaf1(a)-f2(a);doublefmf1(mid)-f2(mid);if(Math.abs(fm)eps)returnmid;if(fa*fm0)bmid;elseamid;}return(ab)/2;}二分法保证收敛且精度可控。对于本例只需几次迭代即可达到1e-10精度。五、交点标注自定义节点5.1 需求使用空心圆圈标记交点位置。在圆圈上方显示坐标标签避免遮挡曲线。5.2 实现为每个交点数据创建一个XYChart.Data并通过setNode设置自定义的StackPane。XYChart.DataNumber,NumberdatanewXYChart.Data(p[0],p[1]);StackPanenodenewStackPane();CirclecirclenewCircle(6);circle.setFill(null);// 空心circle.setStroke(Color.RED);circle.setStrokeWidth(2);LabellabelnewLabel(String.format((%.2f, %.2f),p[0],p[1]));label.setTextFill(Color.BLUE);label.setTranslateY(-20);// 上移node.getChildren().addAll(circle,label);data.setNode(node);5.3 为什么放在最上层先添加曲线系列series1, series2再添加交点系列使得交点绘制在曲线之上避免被覆盖。六、为什么数值解与解析解一致令 ( f_1(x) f_2(x) )[3x^2 - 1 2x - 1 \Rightarrow 3x^2 - 2x 0 \Rightarrow x0 \text{ 或 } x\frac{2}{3}]对应 y 值为 -1 和 1/3。数值解在1e-10精度下完全吻合验证了算法的正确性。这证明了在步长足够小、二分法精度足够高的情况下数值方法能可靠地逼近解析解。七、与之前学习内容的联系Context/ContextView虽未直接用于此程序但理解上下文传递有助于在更复杂的响应式流中传递调试信息。SynchronousSinkFlux.generate曾用于创建无限流而本例使用Flux.range生成有限点集两者都是 Reactor 生成数据的常用方式。Fuseable内部优化对于大量数据点可提升性能但本例数据量不大未显式使用。数值求交延续了之前对数函数导数、三角数流等数值计算的思想体现了响应式流在科学计算中的实用性。八、运行与效果运行程序后窗口显示两条平滑曲线交点处出现红色空心圆圈上方附有蓝色坐标标签清晰无遮挡。控制台无额外输出全部信息可视化。九、总结本文通过一个完整的 Java 程序展示了如何结合Reactor和JavaFX响应式生成大量离散点以绘制平滑曲线。利用数值差分法符号检测 二分法求解函数交点。自定义图表节点实现空心圆圈与标签标注。该方案不依赖解析公式可推广至任意连续函数具有广泛的应用价值。掌握这些技术将为后续的数值分析与可视化开发奠定坚实基础。完整代码publicclassFunctionPlotterextendsApplication{// 定义函数导数 f1(x) 3x² - 1privatedoublef1(doublex){return3*x*x-1;}// 定义函数直线 f2(x) 2x - 1privatedoublef2(doublex){return2*x-1;}Overridepublicvoidstart(StageprimaryStage){// 坐标轴NumberAxisxAxisnewNumberAxis();xAxis.setLabel(x);xAxis.setLowerBound(-5);xAxis.setUpperBound(5);xAxis.setAutoRanging(false);NumberAxisyAxisnewNumberAxis();yAxis.setLabel(y);yAxis.setAutoRanging(true);// 图表LineChartNumber,NumberchartnewLineChart(xAxis,yAxis);chart.setTitle(导数函数与直线交点);chart.setCreateSymbols(false);// 不显示数据点默认符号避免干扰doublestep0.01;doublestartX-5.0;doubleendX5.0;intcount(int)((endX-startX)/step)1;// 系列1f1(x)XYChart.SeriesNumber,Numberseries1newXYChart.Series();series1.setName(f1(x) 3x² - 1);// 系列2f2(x)XYChart.SeriesNumber,Numberseries2newXYChart.Series();series2.setName(f2(x) 2x - 1);// 使用 Reactor 生成数据点Flux.range(0,count).map(i-startXi*step).doOnNext(x-{series1.getData().add(newXYChart.Data(x,f1(x)));series2.getData().add(newXYChart.Data(x,f2(x)));}).blockLast();// ---- 数值求交点 ----Listdouble[]intersectionsfindIntersections(step,startX,endX);// 交点系列自定义节点空心圆圈 坐标标签XYChart.SeriesNumber,NumberintersectionSeriesnewXYChart.Series();intersectionSeries.setName(交点);for(double[]p:intersections){XYChart.DataNumber,NumberdatanewXYChart.Data(p[0],p[1]);// 自定义节点空心圆 标签StackPanenodenewStackPane();CirclecirclenewCircle(6);circle.setFill(null);// 空心circle.setStroke(Color.RED);circle.setStrokeWidth(2);LabellabelnewLabel(String.format((%.2f, %.2f),p[0],p[1]));label.setTextFill(Color.BLUE);label.setStyle(-fx-font-size: 12px; -fx-font-weight: bold;);label.setTranslateY(-20);// 标签上移避免遮挡node.getChildren().addAll(circle,label);data.setNode(node);intersectionSeries.getData().add(data);}// 添加所有系列到图表先添加曲线再添加交点保证交点在最上层chart.getData().addAll(series1,series2,intersectionSeries);// 可选设置交点系列默认样式透明以防默认样式干扰intersectionSeries.getNode().setStyle(-fx-stroke: transparent; -fx-background-color: transparent;);// 显示窗口ScenescenenewScene(chart,800,600);primaryStage.setScene(scene);primaryStage.show();}/** * 数值求解交点差值函数 h(x)f1(x)-f2(x) 的零点 */privateListdouble[]findIntersections(doublestep,doublestartX,doubleendX){Listdouble[]resultnewArrayList();intcount(int)((endX-startX)/step)1;double[]xValsnewdouble[count];double[]hValsnewdouble[count];for(inti0;icount;i){doublexstartXi*step;xVals[i]x;hVals[i]f1(x)-f2(x);}for(inti0;icount-1;i){doubleh1hVals[i];doubleh2hVals[i1];if(h1*h20){doublerootbisect(xVals[i],xVals[i1]);doubleyf1(root);result.add(newdouble[]{root,y});}// 处理恰好为零的点if(Math.abs(h1)1e-12){doubleyf1(xVals[i]);result.add(newdouble[]{xVals[i],y});}}if(Math.abs(hVals[count-1])1e-12){doubleyf1(xVals[count-1]);result.add(newdouble[]{xVals[count-1],y});}returnresult;}/** * 二分法求根精度 1e-10 */privatedoublebisect(doublea,doubleb){doubleeps1e-10;doublemid;intmaxIter100;for(inti0;imaxIter;i){mid(ab)/2;doublefaf1(a)-f2(a);doublefmf1(mid)-f2(mid);if(Math.abs(fm)eps)returnmid;if(fa*fm0)bmid;elseamid;}return(ab)/2;}publicstaticvoidmain(String[]args){launch(args);}}
延伸阅读

更多相关文章

2026/9/20 1:41:42

开源正在重塑AI的权力版图——从美国科技巨头的集体转向说起

2026年7月,硅谷发生了一件耐人寻味的事。 英伟达CEO黄仁勋在X平台上发布了人生第一条推文。推文附上一封公开信,标题是《开放权重与美国在AI领域的领导地位》。签署方包括英伟达、微软、Meta、IBM、Hugging Face、Mistral AI等25家科技公司和机构。此后…

2026/9/20 1:41:46

写了一个全功能的 RocketMQ .NET 客户端:EventHorizon.RocketMQ

目录 前言功能概览它不是官方客户端的替代品快速接入 先启动本地测试环境gRPC:通过 Proxy 连接 RocketMQ 5Remoting:通过 NameServer 发现 Broker多集群或多实例 发送消息消费消息 gRPC 的消费模型 PushConsumer classic Remoting 的消费模型 PushConsu…

2026/9/22 12:50:46

5个坑解决配置痛点,快用下载实战避坑指南

5个坑解决配置痛点,快用下载实战避坑指南 配置环境就卡半天,是不是你也经历过?明明照着教程一步步敲,结果依赖版本冲突、路径报错,半天没跑起来。更扎心的是,面试必问的工程化落地能力,往往就卡在这一步。今天不聊虚的,直接拆解一个用…

2026/9/22 12:50:46

3分钟搞懂破帽遮颜过闹市与手写实现避坑

3分钟搞懂破帽遮颜过闹市与手写实现避坑 面对满屏红色的报错堆栈,你盯着那个诡异的 Exception in thread "main" 发呆吗?别慌,这种“破帽遮颜过闹市”般的尴尬时刻,每个写代码的人都经历过。…

2026/9/22 12:50:46

国外旅游景点推荐系统慢?3个最佳实践解决性能瓶颈

国外旅游景点推荐系统慢?3个最佳实践解决性能瓶颈 复制来的国外旅游景点推荐算法代码,跑在测试环境飞快,一到生产环境直接卡死,日志里全是超时错误。这时候盲目加缓存或换服务器往往没用,因为问题出在数据聚合与排序逻辑的底层实现上。…

2026/9/22 10:02:42

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/22 9:07:39

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/22 0:04:49

输电线路在线监测高频面试题拆解 3秒抓住官方文档重点

输电线路在线监测高频面试题拆解 3秒抓住官方文档重点 官方文档几百页翻到头还是懵?面试问到 输电线路在线监测 的数据链路时,脑子一片空白?别慌,这种 高频面试题 我整理了10年,专门治各种“文档太长抓不住重点”的毛病。…

2026/9/22 0:04:49

中介房源管理系统重构避坑:3个关键步骤搞定API变更

中介房源管理系统重构避坑:3个关键步骤搞定API变更 版本升级后 API 全变了,这种痛只有真做过的人懂。 很多团队在接手老旧房产项目时,最崩溃的不是代码烂,而是底层框架升级后,原本熟悉的接口调用方式彻底失效。 这份 保姆级教程…

2026/9/22 0:04:49

3个坑点带你一文搞懂55gg小游戏源码

3个坑点带你一文搞懂55gg小游戏源码 盯着控制台满屏的红色报错,看着那一长串 StackTrace ,是不是脑子瞬间宕机?别急,这种时候最忌讳的就是盲目改代码。很多刚入行的前端同学,面对 55gg 小游戏这类轻量级 H5…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/21 18:32:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/21 10:29:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码