numpy不内置物理引擎,仅提供高效数组运算;需手动实现牛顿定律与数值积分(如欧拉或verlet法),并严格遵循向量化、维度匹配和内存优化原则。

NumPy 本身不模拟物理,它只提供高效数组运算——所有运动逻辑必须由你用牛顿定律手写,NumPy 负责把加速度、速度、位置的批量更新跑得飞快。
为什么不能直接调用 simulate_particle() 这类函数?
NumPy 没有内置物理引擎。它不是 scipy.integrate.odeint,也不是 pygame 或 simpy。它的角色是:给你一个 np.array 存 1000 个质点的位置,再给你另一个 np.array 存对应受力,然后用一行 accel = forces / mass 算出全部加速度——这才是它不可替代的地方。
常见错误是误以为 np.linalg.solve 或 np.fft 能自动解运动方程;它们只是数学工具,不会识别“这是引力”或“该积分”。你得自己把 F = ma 拆成 a = F/m,再选数值积分方法(如显式欧拉或 Verlet)。
用显式欧拉法更新位置和速度的实操要点
这是最直白的入门写法,适合理解流程,但稳定性差,步长 dt 必须很小(通常 ≤ 0.01 秒)。
-
positions += velocities * dt—— 位置前进一步 -
velocities += accelerations * dt—— 速度前进一步 - 加速度必须在每次循环开始时重新计算,比如万有引力:用
np.sqrt(np.sum((r_i - r_j)**2, axis=1))算距离,再广播除法得单位方向向量 - 避免在循环里用 Python
for遍历每个质点——全部用向量化操作,否则失去 NumPy 意义 - 若出现“粒子突然飞出屏幕”,大概率是
dt太大或力计算没归一化方向向量
用速度 Verlet 法提升稳定性和能量守恒
比欧拉法多一次加速度计算,但对周期性系统(如行星轨道)更可靠,且天然保持总能量波动小。
- 先更新速度一半步:
velocities += 0.5 * accelerations * dt - 再更新位置全步:
positions += velocities * dt - 重新算新位置下的
new_accelerations(关键!不能复用旧值) - 最后补上速度另一半:
velocities += 0.5 * new_accelerations * dt - 注意:力函数必须支持批量输入
positions并返回同长度accelerations,否则无法向量化 - 若用弹簧力
F = -k * (x - x0),确保x0是 shape 匹配的数组,而非标量
内存与性能容易被忽略的陷阱
模拟 10⁴ 个质点两两作用时,直接写 np.outer 或嵌套广播会生成 10⁸ 维中间数组,瞬间爆内存。
- 改用近邻搜索(如
scipy.spatial.cKDTree)只计算距离r 的相互作用 - 重力/库仑力可分离变量:用
np.einsum替代多重reshape,例如np.einsum('ij,ij->i', forces, unit_vectors) - 避免频繁
np.concatenate或np.append——预分配trajectory = np.zeros((n_steps, n_particles, 3)) - 调试时先用
dtype=np.float32节省一半显存,确认逻辑正确后再切回float64
真正卡住你的从来不是公式推导,而是数组维度对不上、广播规则没吃透、或者忘了力是矢量得按分量算——这些细节不手动 print 出中间 shape,光靠脑补很容易错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











