科学运算包括数值运算和符号运算,数值运算可以使用 Numpy 库和 Scipy 库。本篇文章主要介绍如何使用 Python 进行基础的数学运算,例如方程求根、数值积分、常微分方程、偏微分方程等。
数值微分
数值微分使用 numdifftools 包中的 Derivative
函数
1 | import numdifftools as nd |
Derivative
可返回导数函数,而非离散的导数值
1 | nd.Derivative(fun, step=None, method='central', order=2, n=1) |
step
为差分步长,默认为 infermethod
为差分方法,默认为中心差分order
为误差阶数,默认为n
为求导阶数,默认为一阶导数
数值积分
数值积分使用 Scipy 中的 integrate 模块,调用方法为
1 | from scipy import integrate |
一重积分
基本语法
一重积分使用 integrate.quad 函数
1 | integrate.quad(func, a, b, args=(tuple), epsabs, epsrel) |
-
func 可以是单参数函数 或多参数函数 ,如果是后者默认只对第一个变量积分
-
a 和 b 分别是积分上限和积分下限
-
args 是可选参数,若被积函数为多参数函数 ,则须按顺序传入除被积变量以外的参数的值
/* 例:将 在 上积分*/
1
2f = lambda x,t1,t2: t1*x+t2
integrate.quad(f,0,1,args=(2,3)) -
epsabs 为绝对误差限,默认值为 1.49e-8
-
epsrel 为相对误差限,默认值为 1.49e-8
正常情况下,函数的返回值是一个元组,第 0 项为积分值,第 1 项为误差。
对任一变量积分
若要对多参数函数 的非第一个变量 (x) 积分,可以在 func 处使用 lambda
临时定义一个函数。
例:考虑对 的两个变量分别积分,容易算得
对于前者,很容易可以想到通过如下代码实现
1 | # The first integral |
对于后者,可以通过
1 | # The second integral, method one |
或者定义临时的匿名函数时直接给定 ,避免使用 args
1 | # The second integral, method two |
显然第二种方法的代码可读性更高。
二重积分
二重积分使用 integrate.dblquad 函数
1 | integrate.dblquad(func, a, b, gfun, hfun, args=(), epsabs=1.49e-8, epsrel=1.49e-8) |
将计算函数 的如下积分
三重积分
三重积分使用 integrate.tplquad 函数
1 | integrate.tplquad(func, a, b, gfun, hfun, qfun, rfun, args=(), epsabs=1.49e-08, epsrel=1.49e-08) |
将计算函数 的如下积分
方程的数值解
方程数值求根使用 Scipy 中的 optimize 模块
1 | from scipy.optimize import root_scalar |
标量方程
标量方程使用 root_scalar 求根,默认对第一个变量求根
1 | root_scalar(f, args=(), method=None, bracket=None, fprime=None, fprime2=None, x0=None, x1=None) |
- 二分法 ‘bisect’ :braket = [a,b] (A sequence of 2 floats)
- 牛顿法 ‘newton’ :fprime, x0
- 哈雷法 ‘halley’ :fprime, fprime2, x0
- 弦割法 ‘secant’ :x0, x1
线性向量方程
线性方程求解使用 Scipy 中的 linalg 线性代数模块
一般线性方程
使用 scipy.linalg.solve 求解矩阵方程A @ x == b
1 | scipy.linalg.solve(A, b, assume_a='gen', transposed=False) |
说明:assume_a
可指定矩阵类型
matrix type | assume_a |
---|---|
generic matrix | ‘gen’ |
symmetric | ‘sym’ |
hermitian | ‘her’ |
positive definite | ‘pos’ |
若 transposed = True
则求解矩阵方程 A.T @ x == b
。
带状矩阵方程
使用 scipy.linalg.solve_banded 求解带状矩阵方程,使用 scipy.linalg.solveh_banded 求解 Hermite 正定矩阵方程
1 | scipy.linalg.solve_banded(lu, ab, b) |
1 | scipy.linalg.solveh_banded(ab, b) |
lu
是一个元组,表示主对角线下 (lower) 和主对角线上 (upper) ,分别有几行非零次对角线。ab
是带状矩阵的非零矩阵元,遵循 ab[u + i - j, j] == a[i,j]
,是一个 行 列的矩阵。例如对于矩阵方程:
ab
矩阵应当设置为
非线性向量方程
非线性向量方程组的求解使用 scipy.optimize.root 函数,语法为
1 | scipy.optimize.root(fun, x0, args=(), method='hybr', jac=None) |
求解实数非线性方程使用默认方法 'hybr'
即可,求解复数方程可使用 ’krylov’
方法。
jac
参数为待求解方程 的 Jacobi 矩阵,若传入则显著提升计算速度。
预搜索
预搜索可通过以下代码实现
1 | def bisection(f,a,b,xtol,maxiter): |
常微分方程的初值问题
简介
一阶非线性常微分方程的普遍形式为
对于高阶常微分方程,总可以通过降阶化为一阶常微分方程组,例如牛顿第三定律
其中 。因此,关键在于求解一阶非线性常微分方程。
求解器
常微分方程的初值问题使用 scipy.integrate.solve_ivp ,其中 ivp 代表 initial value problem
1 | scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False) |
各参数的意义如下
fun
为导函数 ,t_span
为 A sequence of 2 floats,y0
为初始值method
默认为 4/5 阶 Runge-Kutta 法t_eval
为时间节点,必须已排序切若不指定则由求解器自动分割t_span
并求解dense_output
选择是否对结果进行插值
返回的结果为一个对象,属性包括
t
:时间节点y
:函数值sol
:Odesolution 类