用编程实现计算方法/数值分析的概念知识 (三)
数值积分与数值微分
用复化梯形公式、复化辛普森公式计算积分
$$ I=\int^1_0 \frac{4}{1+x^2}dx $$
解:
# =============================================================================
# 定义区间长度, 子区间数, 步长
l = 1 # 区间长度
n = 8 # 子区间数
h = 1/8 # 步长
# 定义被积函数
def f(x):
return 4/(1+x**2)
# =============================================================================
# 构造字典,存放分点数据
table = {}
for i in range(0,9):
table[i*h] = f(i*(1/8))
# =============================================================================
# 定义复化梯形公式
def T(n, h):
res = table[0]+table[1]
for i in range(1, n):
res += 2*table[i*h]
res*=(1/2 * h)
return res
# 定义复化辛普森公式
def S(n, h):
res = table[0]+table[1]
for i in range(1,n):
res += 2*table[i*h]
for i in range(0,n):
res += 4*table[i*h+h/2]
res*=(1/6 * h)
return res
# ==============================================================================
def main():
print('数据表格如下')
for key in table:
print('f('+str(key)+'):'+str(table[key]))
print('\n')
print('复化梯形公式得:')
print(T(8,1/8))
print('\n')
print('复化辛普森公式得:')
print(S(4,1/4))
if __name__ == '__main__':
main()
但是这个实现方法并不精美,数值积分中每项的系数是变化的,显然不能写死在代码里,所以对自己之前写的代码进行了一段优化。
优化过后:
# =============================================================================
# 定义区间长度, 子区间数, 步长
l = 1 # 区间长度
n = 8 # 子区间数
h = 1/8 # 步长
# =============================================================================
# 定义被积函数
def f_(x):
return 4/(1+x**2)
# 构造字典,存放分点数据
table = {}
# 定义查找函数, 若有则直接返回, 若没有则先计算, 存入字典中, 再返回
def f(x):
if x in table.keys():
return table[x]
else:
res = f_(x)
table[x] = res
return res
# =============================================================================
# 定义复化梯形公式
def T(s, t, n):
res = f(s)+f(t)
len = t-s # 区间总长度
h = len/n # 子区间长度
for i in range(1, n):
res += 2*f(i*h)
res*=(1/2 * h)
return res
# 定义复化辛普森公式
def S(s, t, n):
res = f(s)+f(t)
len = t-s # 区间总长度
h = len/n # 子区间长度
for i in range(1,n):
res += 2*f(i*h)
for i in range(0,n):
res += 4*f(i*h+h/2)
res*=(1/6 * h)
return res
# 定义复化科特斯公式
def C(s, t, n):
res = 7*f(s)+7*f(t)
len = t-s # 区间总长度
h = len/n # 子区间长度
for i in range(0,n):
res += 32*f(i*h+1/4*h)
res += 32*f(i*h+3/4*h)
res += 12*f(i*h+1/2*h)
for i in range(1,n):
res += 14*f(i*h)
res*=(1/90 * h)
return res
def main():
print('复化梯形公式得:')
print(T(0, 1, 8))
print('\n')
print('复化辛普森公式得:')
print(S(0, 1, 4))
print('\n')
print('复化柯特斯公式得:')
print(C(0, 1, 2))
if __name__ == '__main__':
main()
用龙贝格求积公式计算积分
$$ I = \int^1_0\frac{4}{1+x^2}dx $$
解:
# =============================================================================
# 定义区间长度, 子区间数, 步长
l = 1 # 区间长度
n = 8 # 子区间数
h = 1/8 # 步长
# =============================================================================
# 定义被积函数
def f_(x):
return 4/(1+x**2)
# 构造字典,存放分点数据
table = {}
# 定义查找函数, 若有则直接返回, 若没有则先计算, 存入字典中, 再返回
def f(x):
if x in table.keys():
return table[x]
else:
res = f_(x)
table[x] = res
return res
# =============================================================================
# 定义查找梯形公式值, 若有则直接返回, 若没有则先计算, 存入字典中, 再返回
T_tab = {}
S_tab = {}
C_tab = {}
R_tab = {}
def T_(s, t, x):
if x in T_tab.keys():
return T_tab[x]
else:
res = T(s, t, x)
T_tab[x] = res
return res
def S_(s, t, x):
if x in S_tab.keys():
return S_tab[x]
else:
res = ( 4*T_(s, t, 2*x) - T_(s, t, x) )/3
S_tab[x] = res
return res
def C_(s, t, x):
if x in C_tab.keys():
return C_tab[x]
else:
res = ( 16*S_(s, t, 2*x) - S_(s, t, x) )/15
C_tab[x] = res
return res
def R_(s, t, x):
if x in R_tab.keys():
return R_tab[x]
else:
res = ( 64*C_(s, t, 2*x) - C_(s, t, x) )/63
R_tab[x] = res
return res
# =============================================================================
# 定义复化梯形公式
def T(s, t, n):
res = f(s)+f(t)
len = t-s # 区间总长度
h = len/n # 子区间长度
for i in range(1, n):
res += 2*f(i*h)
res*=(1/2 * h)
return res
def main():
err = 1
i = 0
while(err>0.000001):
err = R_(0, 1, i+1)-R_(0, 1, i)
i+=1
print(R_(0, 1, i))
if __name__ == '__main__':
main()
常微分方程数值解法
用欧拉公式求解初值问题
$$ y'=y-\frac{2x}{y},0<x\leqslant1 $$
$$ y(0)=1 $$
取步长$h=0.2$。
解:参数代入公式,求解该问题的欧拉公式是:
$$ y_{i+1}=y_i+h{y_i-\frac{2x_i}{y_i}} $$
$$ y_0=1 $$
编程如下:
# =============================================================================
# 定义该问题的常量
h = 0.2 # 步长
prec_res = 6 # 结果的精度
# =============================================================================
# 定义公式
def Euler(x, y):
return (y + h*(y-2*x/y))
def exact(x):
return (1+2*x)**(1/2)
# =============================================================================
# 用于遍历的量
k = 0
x = 0.0 # 0 < x <= 1
y = round(1,prec_res) # y0=1
ye = (exact(x)) # 精确值
err= abs(y-ye)
print('k', '\t', 'x', '\t\t', 'y', '\t\t', 'ye', '\t\t', 'err')
print(k, '\t', x, '\t', y, '\t\t', ye, '\t\t', err)
err = 100
while x<1:
k += 1
y = Euler(x, y)
x = round(x+h, 1)
ye = exact(x)
err= abs(y-ye)
print(k, '\t', x, '\t', round(y, prec_res), '\t', round(ye, prec_res), '\t', round(err, prec_res))
自己输出的结果:
$k$ | $x$ | $y$ | $y_e$ | $err$ |
---|---|---|---|---|
0 | 0.0 | 1 | 1.0 | 0.0 |
1 | 0.2 | 1.2 | 1.183216 | 0.016784 |
2 | 0.4 | 1.373333 | 1.341641 | 0.031693 |
3 | 0.6 | 1.531495 | 1.48324 | 0.048255 |
4 | 0.8 | 1.681085 | 1.612452 | 0.068633 |
5 | 1.0 | 1.826048 | 1.732051 | 0.094897 |
用高等数学的微分方程解法,可以解得该题的精确解是$y=\sqrt{1+2x}$,跟程序输出相差不大。
- 用改进欧拉公式对上式求值
解:改进欧拉公式其实就是转换运算方向,多求了一个后退的欧拉公式,然后取了平均。附上代码:
# =============================================================================
# 定义该问题的常量
h = 0.2 # 步长
prec_res = 6 # 结果的精度
# =============================================================================
# 定义公式
# 欧拉冲冲冲公式
def Eulerp(x, y):
return (y + h*(y-2*x/y))
# 欧拉且战且退公式
def Eulerc(x, yi, yp):
return (yi + h*(yp-2*x/yp))
# 微分方程精确值
def exact(x):
return (1+2*x)**(1/2)
# =============================================================================
# 用于遍历的量
k = 0
x = 0.0 # 0 < x <= 1
y = round(1,prec_res) # y0=1
ye = (exact(x)) # 精确值
err= abs(y-ye)
print(k, '\t', x, '\t', y, '\t', ye, '\t', err)
err = 100
while x<1:
k += 1
yp = Eulerp(x, y)
x = round(x+h, 1)
yc = Eulerc(x, y, yp)
y=(yp+yc)/2
ye = exact(x)
err= abs(y-ye)
print(k, '\t', x, '\t', round(y, prec_res), '\t', round(ye, prec_res), '\t', round(err, prec_res))
附上输出结果:
$k$ | $x$ | $y$ | $y_e$ | $err$ |
---|---|---|---|---|
0 | 0.0 | 1 | 1.0 | 0.0 |
1 | 0.2 | 1.186667 | 1.183216 | 0.03451 |
2 | 0.4 | 1.348312 | 1.341641 | 0.006671 |
3 | 0.6 | 1.493704 | 1.48324 | 0.010464 |
4 | 0.8 | 1.627861 | 1.612452 | 0.01541 |
5 | 1.0 | 1.754305 | 1.732051 | 0.022154 |
Comments | NOTHING