You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免Scipy的odeint求解常微分方程时得到发散的解

问题描述

使用Scipy的odeint求解不同初始条件下的常微分方程并绘制结果曲线时,初始条件为2、4、6的三组解在衰减到0之后,曲线会出现异常表现。希望修复该问题,让最终会衰减到0的解只保留正常的曲线段,不需要手动为每个初始条件单独设置求解时间区间,且适配odeint接口,不使用solve_ivp。

原始代码如下:

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
ic = [2,4,6,8,10,12,14,16,18,20]
def de(t, u):
    return u*(1-u/12)-4*np.heaviside(-(t-5), 1)

plt.xlim([0, 10])
plt.ylim([0, 20])
for N0 in ic:
    N1 = odeint(de, N0, np.linspace(0, 10, 10000), tfirst=True)
    plt.plot(np.linspace(0,10,10000), N1)
plt.show()

现有手动分段处理的方案不够灵活,无法适配任意初始条件的情况。

优化方案

核心思路是在求解完成后自动截断解的有效区间:

  • 统一生成全局时间数组,避免循环中重复计算
  • 对每个初始条件的求解结果,找到解首次小于等于0的位置
  • 仅绘制从起点到该位置的有效数据段,无需提前拆分初始条件组,也不需要手动设置每个组的求解区间

如果担心浮点数计算精度问题,可以将判断条件调整为N1 <= 1e-6,过滤接近0的极小数值的干扰。

完整优化代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

ic = [2,4,6,8,10,12,14,16,18,20]
def de(t, u):
    return u*(1-u/12)-4*np.heaviside(-(t-5), 1)

# 统一生成时间数组,避免循环中重复创建
t = np.linspace(0, 10, 10000)

plt.xlim([0, 10])
plt.ylim([0, 20])
for N0 in ic:
    # ravel把odeint返回的二维数组转为一维,方便处理
    N1 = odeint(de, N0, t, tfirst=True).ravel()
    # 找到第一个解<=0的索引,所有解都为正的话返回0
    first_neg_idx = np.argmax(N1 <= 0)
    if first_neg_idx > 0:
        # 存在负数值,截断到第一个负数值之前
        plt.plot(t[:first_neg_idx], N1[:first_neg_idx])
    else:
        # 所有解都为正,直接绘制全部
        plt.plot(t, N1)
plt.show()

该方案可以自动适配任意初始条件,不需要手动调整每个初始值对应的求解区间,逻辑简洁高效,完全基于odeint接口实现。

内容的提问来源于stack exchange,提问作者An Ignorant Wanderer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:48:04