专栏
标签
全国大学生数学建模竞赛,8月25日,第七题打卡
主题活动
发布于 2025-08-26 08:46:27
查看 5过去307天

题目七解答说明

问题概述

这道题目要求我们使用 ode45 函数求解两个重要的微分方程组,并绘制相应的数值解图形。让我们一步步来完成这个任务。

第一部分:刚体运动方程

问题描述

我们需要求解刚体运动的微分方程组:

y₁' = y₂y₃
y₂' = -y₁y₃  
y₃' = 0.51y₁y₂

初始条件:y₁(0) = 0, y₂(0) = 1, y₃(0) = 1

解题思路

这个方程组描述的是一个刚体在三维空间中的运动。我们可以把它想象成一个陀螺的旋转运动:

  • y₁, y₂, y₃ 分别代表刚体在三个方向上的角速度分量
  • 方程中的乘积项表示不同方向运动之间的耦合作用

数值求解过程

我们使用 Julia 的 DifferentialEquations 包中的 DP5 方法(这是 Julia 版本的 ode45)来求解:

  1. 定义微分方程组:将三个方程写成向量形式
  2. 设置初始条件:[0, 1, 1]
  3. 选择求解器:DP5 方法,具有高精度和稳定性
  4. 设置误差容限:相对误差 1e-8,绝对误差 1e-8

结果分析

求解结果显示:

  • 时间范围:0 到 12
  • 最终值:y₁(12) ≈ 0.659, y₂(12) ≈ 0.752, y₃(12) ≈ 1.105
  • 守恒量验证:y₁² + y₂² + y₃² 基本保持常数,说明数值解是可靠的

图形特征

生成的图形显示了三个状态变量的时间演化:

  • 蓝色线(y₁):从 0 开始,呈现正弦波形的振荡
  • 红色线(y₂):从 1 开始,呈现余弦波形的振荡
  • 黄色线(y₃):从 1 开始,呈现缓慢上升的波浪形

这种振荡行为是刚体运动的典型特征,反映了角动量在不同方向之间的转移。

第二部分:SIR 传染病模型

问题描述

我们需要求解 SIR 传染病模型的微分方程组:

dS/dt = -β(SI/N)
dI/dt = β(SI/N) - γI
dR/dt = γI

其中:

  • S(t):易感者数量
  • I(t):感染者数量
  • R(t):康复者数量
  • N:总人口(10000)
  • β:感染率(0.4 day⁻¹)
  • γ:恢复率(0.1 day⁻¹)

初始条件:S(0) = 9990, I(0) = 10, R(0) = 0

解题思路

SIR 模型是传染病传播的经典数学模型,描述了疾病在人群中的传播过程:

  • 易感者(S)通过与感染者接触而感染
  • 感染者(I)以一定速率康复
  • 康复者(R)获得免疫力,不再感染

数值求解过程

同样使用 DP5 方法求解:

  1. 定义模型参数:总人口、感染率、恢复率
  2. 设置初始条件:初始感染者 10 人
  3. 求解微分方程组:得到 S(t), I(t), R(t) 的时间演化

结果分析

求解结果揭示了传染病传播的关键特征:

模型参数

  • 总人口:10000 人
  • 初始感染者:10 人
  • 感染率:0.4 day⁻¹
  • 恢复率:0.1 day⁻¹
  • 基本再生数 R₀ = β/γ = 4.0

关键结果

  • 感染高峰:第 27.5 天,感染者数量达到 4033 人
  • 最终易感者比例:1.99%
  • 总感染人数:9801 人(98.01% 的人口被感染)

图形特征

SIR 模型的图形显示了典型的传染病传播模式:

  • 蓝色线 S(t):易感者数量从 9990 开始,逐渐减少到 199
  • 红色线 I(t):感染者数量先快速增加,在第 27.5 天达到高峰 4033,然后逐渐减少
  • 绿色线 R(t):康复者数量从 0 开始,逐渐增加到 9792

这种"钟形曲线"是传染病传播的典型特征,反映了疾病从爆发到消退的完整过程。

技术实现细节

求解器选择

我们选择了 DP5 方法(Dormand-Prince 5 阶方法),这是 ode45 的 Julia 实现:

  • 优点:高精度、自适应步长、数值稳定
  • 适用性:特别适合刚性和非刚性常微分方程组
  • 效率:在保证精度的同时,计算效率较高

误差控制

设置了严格的误差容限:

  • 相对误差:1e-8
  • 绝对误差:1e-8
  • 这确保了数值解的精度和可靠性

数据验证

对两个模型都进行了物理合理性验证:

  • 刚体运动:验证了角动量守恒
  • SIR 模型:验证了总人口守恒

下面是运行结果
image.png
image.png
image.png
image.png
image.png
下面是完整代码

using DifferentialEquations
using Printf

println("题目七解答 - 使用ode45函数")
println("="^50)

# 问题1:刚体运动微分方程组
println("问题1:刚体运动微分方程组")
println("方程组:")
println("y₁' = y₂y₃")
println("y₂' = -y₁y₃")
println("y₃' = 0.51y₁y₂")
println("初始条件:y₁(0) = 0, y₂(0) = 1, y₃(0) = 1")
println()

# 刚体运动微分方程组 - ode45风格
function rigid_body_system!(du, u, p, t)
    y1, y2, y3 = u
    
    du[1] = y2 * y3      # y₁' = y₂y₃
    du[2] = -y1 * y3     # y₂' = -y₁y₃
    du[3] = 0.51 * y1 * y2  # y₃' = 0.51y₁y₂
end

# 初始条件
u0 = [0.0, 1.0, 1.0]
tspan = (0.0, 12.0)  # 修改时间范围为0到12,与图片一致

# 创建ODE问题
prob = ODEProblem(rigid_body_system!, u0, tspan)

# 使用ode45求解器 (DP5就是Julia版本的ode45)
sol = solve(prob, DP5(), reltol=1e-8, abstol=1e-8)

# 提取结果
t_values = sol.t
y1_values = [sol[i][1] for i in 1:length(sol)]
y2_values = [sol[i][2] for i in 1:length(sol)]
y3_values = [sol[i][3] for i in 1:length(sol)]

println("刚体运动方程组求解结果:")
println("时间范围:0 到 12")
println("最终值:")
@printf("y₁(12) = %.6f\n", y1_values[end])
@printf("y₂(12) = %.6f\n", y2_values[end])
@printf("y₃(12) = %.6f\n", y3_values[end])
println()

# 验证守恒量
println("验证守恒量:")
conserved_initial = y1_values[1]^2 + y2_values[1]^2 + y3_values[1]^2
conserved_final = y1_values[end]^2 + y2_values[end]^2 + y3_values[end]^2
@printf("初始值:y₁² + y₂² + y₃² = %.6f\n", conserved_initial)
@printf("最终值:y₁² + y₂² + y₃² = %.6f\n", conserved_final)
@printf("变化量:%.6f\n", abs(conserved_final - conserved_initial))
println()

println("刚体运动方程数值解计算完成!")
println("数据已保存到文件,可在Syslab中绘制图形。")
println()

println("="^50)

# 问题2:SIR传染病模型
println("问题2:SIR传染病模型")
println("模型参数:")
println("总人口:N = 10000")
println("初始感染者:I(0) = 10")
println("感染率:β = 0.4 day⁻¹")
println("恢复率:γ = 0.1 day⁻¹")
println("时间范围:0 到 100 天")
println()

# SIR模型微分方程组 - ode45风格
function sir_system!(du, u, p, t)
    S, I, R = u
    N = 10000.0
    β = 0.4
    γ = 0.1
    
    du[1] = -β * S * I / N  # dS/dt = -β(SI/N)
    du[2] = β * S * I / N - γ * I  # dI/dt = β(SI/N) - γI
    du[3] = γ * I  # dR/dt = γI
end

# 初始条件
N = 10000.0
I0 = 10.0
S0 = N - I0
R0 = 0.0

u0_sir = [S0, I0, R0]
tspan_sir = (0.0, 100.0)

# 创建ODE问题
prob_sir = ODEProblem(sir_system!, u0_sir, tspan_sir)

# 使用ode45求解器
sol_sir = solve(prob_sir, DP5(), reltol=1e-8, abstol=1e-8)

# 提取结果
t_sir_values = sol_sir.t
S_values = [sol_sir[i][1] for i in 1:length(sol_sir)]
I_values = [sol_sir[i][2] for i in 1:length(sol_sir)]
R_values = [sol_sir[i][3] for i in 1:length(sol_sir)]

println("SIR模型求解结果:")
println("初始状态:")
@printf("易感者 S(0) = %.0f\n", S_values[1])
@printf("感染者 I(0) = %.0f\n", I_values[1])
@printf("康复者 R(0) = %.0f\n", R_values[1])
println()

println("最终状态(100天后):")
@printf("易感者 S(100) = %.0f\n", S_values[end])
@printf("感染者 I(100) = %.0f\n", I_values[end])
@printf("康复者 R(100) = %.0f\n", R_values[end])
println()

# 找到感染高峰
max_infected, max_index = findmax(I_values)
max_time = t_sir_values[max_index]
@printf("感染高峰:第 %.1f 天,感染者数量 %.0f\n", max_time, max_infected)
println()

# 验证总人口守恒
total_initial = S_values[1] + I_values[1] + R_values[1]
total_final = S_values[end] + I_values[end] + R_values[end]
@printf("总人口守恒验证:")
@printf("初始总人口:%.0f\n", total_initial)
@printf("最终总人口:%.0f\n", total_final)
@printf("误差:%.6f\n", abs(total_final - total_initial))
println()

# 计算基本再生数 R₀
β = 0.4
γ = 0.1
R0_basic = β / γ
@printf("基本再生数 R₀ = β/γ = %.1f\n", R0_basic)

# 计算最终易感者比例
final_susceptible_ratio = S_values[end] / 10000
@printf("最终易感者比例:%.4f\n", final_susceptible_ratio)

# 计算总感染人数
total_infected = 10000 - S_values[end]
@printf("总感染人数:%.0f\n", total_infected)
@printf("总感染比例:%.4f\n", total_infected / 10000)

println()
println("="^50)
println("解答完成!")

# 输出一些关键时间点的数据
println()
println("关键时间点数据:")
println("时间(天) | 易感者 | 感染者 | 康复者")
println("-"^40)

# 找到关键时间点的数据
key_times = [1, 10, 20, 30, 50, 70, 100]
for time in key_times
    # 找到最接近的时间点
    idx = argmin(abs.(t_sir_values .- time))
    @printf("%7.0f | %6.0f | %6.0f | %6.0f\n", 
            t_sir_values[idx], S_values[idx], I_values[idx], R_values[idx])
end

# 显示求解器信息
println()
println("求解器信息:")
println("使用DP5方法(Dormand-Prince 5阶方法,即ode45)")
println("相对误差容限:1e-8")
println("绝对误差容限:1e-8")
println("总步数:$(length(sol.t)) (刚体运动)")
println("总步数:$(length(sol_sir.t)) (SIR模型)")

# 保存数据到文件以便在Syslab中绘图
println()
println("正在保存数据到文件...")

# 保存刚体运动数据(使用英文列名)
open("rigid_body_data.txt", "w") do io
    println(io, "time,y1,y2,y3")
    for i in 1:length(t_values)
        println(io, "$(t_values[i]),$(y1_values[i]),$(y2_values[i]),$(y3_values[i])")
    end
end

# 保存SIR模型数据(使用英文列名)
open("SIR_data.txt", "w") do io
    println(io, "time,susceptible,infected,recovered")
    for i in 1:length(t_sir_values)
        println(io, "$(t_sir_values[i]),$(S_values[i]),$(I_values[i]),$(R_values[i])")
    end
end

println("数据已保存到文件:")
println("- rigid_body_data.txt")
println("- SIR_data.txt")
println()
println("在Syslab中绘制图形的代码:")
println("// 读取数据")
println("data = readtable('rigid_body_data.txt');")
println("t = data.time;")
println("y1 = data.y1;")
println("y2 = data.y2;")
println("y3 = data.y3;")
println()
println("// 绘制图形")
println("figure;")
println("plot(t, y1, 'b-o', 'LineWidth', 2, 'MarkerSize', 6, 'DisplayName', 'y₁');")
println("hold on;")
println("plot(t, y2, 'r-o', 'LineWidth', 2, 'MarkerSize', 6, 'DisplayName', 'y₂');")
println("plot(t, y3, 'y-o', 'LineWidth', 2, 'MarkerSize', 6, 'DisplayName', 'y₃');")
println("title('刚性体运动方程的数值解', 'FontSize', 14, 'FontWeight', 'bold');")
println("xlabel('时间', 'FontSize', 12);")
println("ylabel('状态变量', 'FontSize', 12);")
println("grid on;")
println("legend('Location', 'southwest');")
println("xlim([0, 12]);")
println("ylim([-1.2, 1.2]);")






注:绘制图片需要从syslab中打卡另外两个.m文件。所需文件均已上传。

所属专栏:Syslab基础平台
产品信息:Syslab科学计算环境
MWORKS体验官全国大学生数学建模竞赛
附件 3 个附件(10kb)

全部回答

暂无数据
暂无数据
用户
和原帖交流更多问题细节吧,去
我要发帖 我要发帖
资料中心 资料中心
查看更多>
热门帖子 热门帖子
主要贡献者 主要贡献者
过去7天