龙格-库塔四阶法
Runge–Kutta fourth-order method
我正在为双方程组实现 Runge–Kutta 四阶方法。
h 是段数,所以 T/h 是步长。
def cauchy(f1, f2, x10, x20, T, h):
x1 = [x10]
x2 = [x20]
for i in range(1, h):
k11 = f1((i-1)*T/h, x1[-1], x2[-1])
k12 = f2((i-1)*T/h, x1[-1], x2[-1])
k21 = f1((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k11, x2[-1] + T/h/2*k12)
k22 = f2((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k11, x2[-1] + T/h/2*k12)
k31 = f1((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k21, x2[-1] + T/h/2*k22)
k32 = f2((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k21, x2[-1] + T/h/2*k22)
k41 = f1((i-1)*T/h + T/h, x1[-1] + T/h*k31, x2[-1] + T/h*k32)
k42 = f2((i-1)*T/h + T/h, x1[-1] + T/h*k31, x2[-1] + T/h*k32)
x1.append(x1[-1] + T/h/6*(k11 + 2*k21 + 2*k31 + k41))
x2.append(x2[-1] + T/h/6*(k12 + 2*k22 + 2*k32 + k42))
return x1, x2
那我在这个系统上测试一下:
def f1(t, x1, x2):
return x2
def f2(t, x1, x2):
return -x1
def true_x1(t):
return np.cos(t) + np.sin(t)
def true_x2(t):
return np.cos(t) - np.sin(t)
它似乎工作正常(我还用不同的初始值和不同的功能测试了它:一切正常):
x10 = 1
x20 = 1
T = 1
h = 10
x1, x2 = cauchy(f1, f2, x10, x20, T, h)
t = np.linspace(0, T, h)
plt.xlabel('t')
plt.ylabel('x1')
plt.plot(t, true_x1(t), "blue", label="true_x1")
plt.plot(t, x1, "red", label="approximation_x1")
plt.legend(bbox_to_anchor=(0.97, 0.27))
plt.show()
plt.xlabel('t')
plt.ylabel('x2')
plt.plot(t, true_x2(t), "blue", label="true_x2")
plt.plot(t, x2, "red", label="approximation_x2")
plt.legend(bbox_to_anchor=(0.97, 0.97))
plt.show()
然后我想检查错误是否在O(step^4)
的数量级上,所以我减少了步骤并计算了这样的错误:
step = []
x1_error = []
x2_error = []
for segm in reversed(range(10, 1000)):
x1, x2 = cauchy(f1, f2, x10, x20, T, segm)
t = np.linspace(0, T, segm)
step.append(1/segm)
x1_error.append(np.linalg.norm(x1 - true_x1(t), np.inf))
x2_error.append(np.linalg.norm(x2 - true_x2(t), np.inf))
我明白了:
plt.plot(step, x1_error, label="x1_error")
plt.plot(step, x2_error, label="x2_error")
plt.legend()
所以,误差是线性的。这真的很奇怪,因为它应该是 O(step^4)
的顺序。谁能告诉我我做错了什么?
for i in range(1, h):
这将从 1
迭代到 h-1
。由于缺少最后一步,时间 T-T/h
时的 x[h-1]
与时间 T
时的精确解的差异是 O(T/h)
.
因此使用
for i in range(1,h+1):
从 i-1
到 i
或
的 h
步
for i in range(h):
从 i
到 i+1
的 h
步。
此外,np.linspace(0,1,4)
将生成 4
个等距数字,其中第一个是 0
,最后一个是 1
,结果是
array([ 0. , 0.33333333, 0.66666667, 1. ])
这可能不是您所期望的。因此通过上述更正使用
t = np.linspace(0, T, segm+1)
在两次计算中使用相同的时间点。
如果您按照通常的含义使用这些字母,那么遵循您的代码会更容易,其中 h
或 dt
是步长,N
是步长的数量脚步。然后在循环前定义h=T/N
或dt=T/N
,避免在函数调用中重复使用T/N
。
我正在为双方程组实现 Runge–Kutta 四阶方法。
h 是段数,所以 T/h 是步长。
def cauchy(f1, f2, x10, x20, T, h):
x1 = [x10]
x2 = [x20]
for i in range(1, h):
k11 = f1((i-1)*T/h, x1[-1], x2[-1])
k12 = f2((i-1)*T/h, x1[-1], x2[-1])
k21 = f1((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k11, x2[-1] + T/h/2*k12)
k22 = f2((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k11, x2[-1] + T/h/2*k12)
k31 = f1((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k21, x2[-1] + T/h/2*k22)
k32 = f2((i-1)*T/h + T/h/2, x1[-1] + T/h/2*k21, x2[-1] + T/h/2*k22)
k41 = f1((i-1)*T/h + T/h, x1[-1] + T/h*k31, x2[-1] + T/h*k32)
k42 = f2((i-1)*T/h + T/h, x1[-1] + T/h*k31, x2[-1] + T/h*k32)
x1.append(x1[-1] + T/h/6*(k11 + 2*k21 + 2*k31 + k41))
x2.append(x2[-1] + T/h/6*(k12 + 2*k22 + 2*k32 + k42))
return x1, x2
那我在这个系统上测试一下:
def f1(t, x1, x2):
return x2
def f2(t, x1, x2):
return -x1
def true_x1(t):
return np.cos(t) + np.sin(t)
def true_x2(t):
return np.cos(t) - np.sin(t)
它似乎工作正常(我还用不同的初始值和不同的功能测试了它:一切正常):
x10 = 1
x20 = 1
T = 1
h = 10
x1, x2 = cauchy(f1, f2, x10, x20, T, h)
t = np.linspace(0, T, h)
plt.xlabel('t')
plt.ylabel('x1')
plt.plot(t, true_x1(t), "blue", label="true_x1")
plt.plot(t, x1, "red", label="approximation_x1")
plt.legend(bbox_to_anchor=(0.97, 0.27))
plt.show()
plt.xlabel('t')
plt.ylabel('x2')
plt.plot(t, true_x2(t), "blue", label="true_x2")
plt.plot(t, x2, "red", label="approximation_x2")
plt.legend(bbox_to_anchor=(0.97, 0.97))
plt.show()
然后我想检查错误是否在O(step^4)
的数量级上,所以我减少了步骤并计算了这样的错误:
step = []
x1_error = []
x2_error = []
for segm in reversed(range(10, 1000)):
x1, x2 = cauchy(f1, f2, x10, x20, T, segm)
t = np.linspace(0, T, segm)
step.append(1/segm)
x1_error.append(np.linalg.norm(x1 - true_x1(t), np.inf))
x2_error.append(np.linalg.norm(x2 - true_x2(t), np.inf))
我明白了:
plt.plot(step, x1_error, label="x1_error")
plt.plot(step, x2_error, label="x2_error")
plt.legend()
所以,误差是线性的。这真的很奇怪,因为它应该是 O(step^4)
的顺序。谁能告诉我我做错了什么?
for i in range(1, h):
这将从 1
迭代到 h-1
。由于缺少最后一步,时间 T-T/h
时的 x[h-1]
与时间 T
时的精确解的差异是 O(T/h)
.
因此使用
for i in range(1,h+1):
从 i-1
到 i
或
h
步
for i in range(h):
从 i
到 i+1
的 h
步。
此外,np.linspace(0,1,4)
将生成 4
个等距数字,其中第一个是 0
,最后一个是 1
,结果是
array([ 0. , 0.33333333, 0.66666667, 1. ])
这可能不是您所期望的。因此通过上述更正使用
t = np.linspace(0, T, segm+1)
在两次计算中使用相同的时间点。
如果您按照通常的含义使用这些字母,那么遵循您的代码会更容易,其中 h
或 dt
是步长,N
是步长的数量脚步。然后在循环前定义h=T/N
或dt=T/N
,避免在函数调用中重复使用T/N
。