为什么scipy.integrate.solve_ivp(RK45)的精度远低于自制RK4实现?
嘿,这个问题确实有点反直觉,毕竟按理论阶数来说5阶的RK45怎么也该比4阶的RK4表现更好对吧?我帮你拆解下可能的原因:
1. 自适应步长的“效率优先” vs 固定小步长的“稳定累积”
你自己写的RK4用的是固定步长0.01,每一步都严格按这个间隔计算;但scipy的RK45是自适应步长算法,它会默认用atol=1e-6和rtol=1e-3的误差容差自动调整步长——只要单步误差在容差范围内,就会尽量用大步长来提升效率。
但你的仿真时长长达300秒(差不多48个圆周周期),属于长期保守系统仿真。虽然RK45单步误差阶数更高,但大步长带来的累积误差在长期运行中会被放大,反而超过固定小步长RK4的累积误差。尤其是圆周运动这种需要严格维持速度大小、角动量的系统,步长的波动会让轨迹的偏离越来越明显。
你可以试试给solve_ivp加更严格的误差限制,比如:
result = solve_ivp( fun=get_v_and_a, t_span=[0, end_time], y0=initial_values, method="RK45", t_eval=time_points, atol=1e-9, rtol=1e-12 )
强制RK45用更小的步长,应该能看到它的轨迹贴近圆周了。
2. 插值过程的额外误差
你设置了t_eval=time_points,要求solve_ivp在每个0.01秒的时间点输出结果,但RK45内部是用自适应步长计算的,最终结果是通过插值得到的。如果内部步长比0.01大,插值过程会引入额外误差,这也是它结果不如你自制RK4的原因之一——你的RK4直接在每个时间点计算,没有插值环节。
3. 保守系统的积分特性
你的问题是典型的保守系统(加速度垂直于速度,速度大小始终为10,角动量守恒)。虽然RK4和RK45都不是辛积分方法(专门针对保守系统的积分方法),但固定步长的RK4在长期仿真中对守恒量的保持会更稳定;而自适应步长的RK45因为不断调整步长,相当于频繁改变积分规则,会让守恒量的漂移速度加快,最终轨迹偏离圆周的程度更严重。
如果想让solve_ivp在这类问题上表现更好,除了调小容差,还可以试试用更高阶的自适应步长方法比如DOP853,或者干脆用固定步长的积分实现(不过solve_ivp没有提供固定步长的RK45选项)。
先试试调小atol和rtol,应该就能看到RK45的结果明显改善啦~
备注:内容来源于stack exchange,提问作者平田智剛

