当前位置: 首页 > news >正文

用SymPy自动求解追及问题的方程

痛点场景还原

假设做一个最经典的追及动画:甲从原点出发,速度 v1=2;乙从 x=10 处同向出发,速度 v2=5,问多久追上。

如果用纯手工方式写Manim

class PainfulCatchUp(Scene): def construct(self): # 手动列方程并求解 # 设 t 为乙出发后的时间,甲的位置:2*(t+?),乙的位置:10+5*t # 如果同时出发:2t = 10+5t → t = -10/3 负数无意义 # 改甲先出发2秒:2(t+2) = 10+5t → 2t+4=10+5t → -3t=6 → t=-2 还是负 # 必须反复调整题设,手算满足实际情况的初始条件 # 这里干脆让乙追甲,甲在乙前面: # 甲在x=10以v=2向前,乙在x=0以v=5同时出发 → 5t = 10+2t → 3t=10 → t=10/3 v1, v2 = 2, 5 t_meet = 10/3 # 手动解出的结果 meet_x = 5 * t_meet # 再手动算相遇位置 # 甲和乙的轨迹只能硬编码 def pos1(t): return 10 + v1 * t def pos2(t): return v2 * t # 然后创建动画……

痛点很明显:

  • 每次改变速度或初始距离,都要重新手写方程、求解、算相遇坐标。
  • 题目条件稍微变化(比如“甲先走1分钟”、“乙在中途休息”),手算的过程就得全部推翻重来。
  • 容易在单位换算、正方向等细节上出错,动画一旦跑起来发现不对,排查起来也费劲。

这些计算本质上就是根据文字描述建立代数方程并求解,正是SymPy最擅长的事。

2. SymPy 解决方案介绍

SymPy可以让我们用符号把追及问题“翻译”成方程,然后自动求解。

import sympy as sp # 符号定义:t 为乙出发后经过的时间 t = sp.symbols('t', positive=True) v1, v2 = 2, 5 # 速度 s0 = 10 # 初始距离(甲在乙前面10米) # 甲的位置:先出发0秒(即同时出发),位置 = s0 + v1*t pos1 = s0 + v1 * t # 乙的位置:从0开始,位置 = v2*t pos2 = v2 * t # 相遇条件:位置相等 eq = sp.Eq(pos1, pos2) solution = sp.solve(eq, t) # 输出: [10/3]

如果甲先出发 2 秒,方程只需改一下:

t_delay = 2 # 甲早出发2秒 pos1 = s0 + v1 * (t + t_delay) # 甲多走2秒 eq = sp.Eq(pos1, pos2) solution = sp.solve(eq, t) # 输出: [14/3]

无论怎么变化,我们只需要修改符号表达式的构建逻辑,求解交给solve,相遇坐标直接代入即可。

接下来把这个思想嵌入Manim,动画就能自适应任意追及条件。

3. Manim 联动实战

下面是一个完整的动画场景:给定甲、乙的初始位置、速度和出发延迟,自动计算相遇点,并动态展示追及过程。

from manim import * import sympy as sp class CatchUpLab(Scene): def construct(self): # ========== 题目参数(任意修改这里即可) ========== v1 = 1.5 # 甲的速度 v2 = 2.5 # 乙的速度 init_gap = 8 # 初始距离(甲在乙前面) delay = 1 # 甲早出发的时间 # ========== SymPy 自动求解 ========== t = sp.symbols("t", positive=True) pos1_expr = init_gap + v1 * (t + delay) # 甲的位置 pos2_expr = v2 * t # 乙的位置 eq = sp.Eq(pos1_expr, pos2_expr) t_meet = sp.solve(eq, t)[0] # 精确解 meet_x = float(pos2_expr.subs(t, t_meet)) # 相遇位置 # 为了动画流畅,预先计算两个运动函数(可以直接用lambda) def pos1_func(time): return init_gap + v1 * (time + delay) def pos2_func(time): return v2 * time # ========== 场景搭建 ========== axes = NumberLine( x_range=[0, 30, 5], length=8, include_numbers=True, label_direction=DOWN, ) self.play(Create(axes)) # 甲和乙的点 dot1 = Dot(color=RED, radius=0.2) dot2 = Dot(color=BLUE, radius=0.2) # 初始放置 dot1.move_to(axes.number_to_point(pos1_func(0))) dot2.move_to(axes.number_to_point(pos2_func(0))) self.add(dot1, dot2) # 标签 label1 = Text("甲", color=RED).next_to(dot1, UP * 2) label2 = Text("乙", color=BLUE).next_to(dot2, UP * 2) self.add(label1, label2) # 轨迹虚线(预留) trace1 = TracedPath(dot1.get_center, stroke_color=RED, stroke_width=2) trace2 = TracedPath(dot2.get_center, stroke_color=BLUE, stroke_width=2) self.add(trace1, trace2) # 相遇点标记(先隐藏,等追到时再显示) meet_dot = Dot(point=axes.number_to_point(meet_x), color=YELLOW) meet_label = Text(f"相遇点: {meet_x:.2f}", font_size=24, color=YELLOW) meet_label.next_to(meet_dot, UP * 1.5) # 动态更新的时间显示 time_text = MathTex("t=0.0").shift(UL * 2) self.add(time_text) # 追击动画 total_time = float(t_meet) + 2 # 多跑2秒 def update_dots(mob, alpha): # alpha 从0到1,对应时间从0到total_time t_now = alpha * total_time dot1.move_to(axes.number_to_point(pos1_func(t_now))) dot2.move_to(axes.number_to_point(pos2_func(t_now))) # 更新标签位置 label1.next_to(dot1, UP * 2) label2.next_to(dot2, UP * 2) # 更新时间显示 time_text.become(MathTex(f"t={t_now:.1f}").shift(UL * 2)) # 判断是否到达相遇点 if t_now >= float(t_meet): self.add(meet_dot, meet_label) # 显示相遇标记 self.play( UpdateFromAlphaFunc( VGroup(dot1, dot2, label1, label2, time_text), update_dots, run_time=total_time, rate_func=linear, ) ) self.wait(1)

http://www.jsqmd.com/news/1090616/

相关文章:

  • esp32开发与应用(esp和wch芯片的配合)
  • 3种方案彻底解决海外镜像拉取失败:DaoCloud镜像加速服务深度实测
  • 终极游戏存档备份指南:为什么Ludusavi v0.29.0是玩家的必备工具?
  • 暗黑3战斗自动化革命:D3KeyHelper如何让重复操作成为历史
  • AFE5808A超声模拟前端芯片ADC与VCA寄存器配置实战指南
  • OpCore-Simplify:三分钟完成黑苹果EFI配置的终极自动化工具
  • 【小白也能轻松玩转龙虾】虾壳云一键部署私人助理,个人电脑搭建 OpenClaw v2.7.9 智能程序(附最新安装包)
  • 2026年竹笋批发供应商怎么选?长期稳定供货看这几项
  • Nginx从入门到精通:一文搞懂这款高性能Web服务器的核心原理与实战配置
  • Java的JNI调用本地方法:性能优化与内存管理
  • 为什么选择OmenSuperHub?一个免费开源工具彻底解决惠普游戏本性能限制问题
  • 完成发射班的焊接及调试
  • 深入 Claude Code 源码(五):MCP 协议——Claude Code 连接外部世界的方式
  • 【Flutter零基础入门 | Day03】常用功能与滚动组件
  • 【claude code实践】 写好第一条提示词:用清晰目标减少返工
  • 鸿蒙 ArkTS 实战:Study Seat Log 从状态建模到交互闭环完整解析
  • 安全组网建设怎么选
  • 终极指南:如何在Mac上禁用Turbo Boost以降低温度和风扇噪音
  • Burp Suite Repeater实战指南:HTTP请求精细调试与渗透测试技巧
  • 【RuoYi-Vue-Plus】源码探秘:OSS配置从数据库到Redis的缓存同步机制
  • MSP430 Timer_A定时器深度解析:从PWM生成到捕获比较实战
  • 推荐系统基础算法简介
  • Win11Debloat:免费快速的Windows系统优化终极方案
  • ChatGPT Plus订阅取消后仍扣费?:2024年Q2真实案例拆解——Stripe账单延迟同步、OpenAI后台状态不同步、第三方渠道(如微软商店)独立续费链路揭秘
  • 2026嵌入式培训机构怎么选?全国机构对比了7家之后,我为什么只推荐金橙智能
  • Embedding Model(嵌入模型)完整讲解:句子转向量全过程
  • Qt 铁甲阅读器-搜索
  • 魔兽世界玩家必备:免费API查询与智能宏生成工具完全指南
  • Go语言的sync.Map缓存使用
  • AI应用开发平台排行榜:企业选型必看指南