如何在 For 循环中重复迭代,Python 3
How to Repeat an Iteration in a For loop, Python 3
我目前正在将具有自适应步长的数值方法 RKF45 (Runge-Kutta-Fehlberg-45) 实施到 python 3 中,我相信我 运行 陷入了一个基本的循环问题我无法解决。请注意,我在实现该数值方法时遇到困难的部分是自适应步长。我了解如何实现这一点的基本算法,我将提供该算法,但首先让我们看一下我构建的执行 RF45 计算的函数:
def rkf45(n): # here we perform the necessary RKF45 computations
t0 = 0
t1 = 5 # this is just a label for the endpoint, not the i = 1 point.
y0 = 0
TOL = 5e-7
h = (t1 - t0) / n
vect = [0] * (n + 1)
vectw = [0] * (n + 1)
vect[0] = t = t0
vectw[0] = y = y0
for i in range(1, n + 1):
k1 = h * gf.f(t, y)
k2 = h * gf.f(t + (1/4) * h, y + (1/4) * k1)
k3 = h * gf.f(t + (3/8) * h, y + (3/32) * k1 + (9/32) * k2)
k4 = h * gf.f(t + (12/13) * h, y + (1932/2197) * k1 - (7200/2197) * k2 + (7296/2197) * k3)
k5 = h * gf.f(t + h, y + (493/216) * k1 - 8 * k2 + (3680/513) * k3 - (845/4104) * k4)
k6 = h * gf.f(t + (1/2) * h, y - (8/27) * k1 + 2 * k2 - (3544/2565) * k3 + (1859/4104) * k4 - (11/40) * k5)
er = (1/h) * ((1/360) * k1 - (128/4275) * k3 - (2197/7540) * k4 + (1/50) * k5 + (2/55) * k6)
# adaptive step size test goes here
vect[i] = t = t0 + i * h
vectw[i] = y = y + ((16/135) * k1 + (6656/12825) * k3 + (28561/56430) * k4 - (9/50) * k5 + (2/55) * k6)
return vect, vectw
请注意,gf.f
是我在一个单独的模块上定义的函数,该模块由:
def f(t, y):
a = -3 * t * y ** 2
b = 1 / (1 + t ** 3)
return a + b
现在,我评论 # adaptive step size goes here
的地方是我的问题所在:我需要测试是否 abs(er) > TOL
,如果这是真的,更新当前步长 h
h = h * q
其中 q = (TOL / (2 * abs(er))) ** (1 / 4)
和 使用此更新的步长重复 当前迭代,直到 abs(er) < TOL
。从那里开始,我需要在下一次迭代中使用这个更新的 h
。
我已经尝试使用 while
循环来实现这一点,但我肯定没有正确实现它;可能是因为我是新手,犯了一个愚蠢的错误。我还尝试使用 if
语句来测试是否 abs(er) > TOL
并从那里更新 h
但我不相信这会强制 for 循环使用更新的 h
.
如果我明白你需要什么:
def rkf45(n): # here we perform the necessary RKF45 computations
t0 = 0
t1 = 5 # this is just a label for the endpoint, not the i = 1 point.
y0 = 0
TOL = 5e-7
h = (t1 - t0) / n
vect = [0] * (n + 1)
vectw = [0] * (n + 1)
vect[0] = t = t0
vectw[0] = y = y0
for i in range(1, n + 1):
# Will loop until break ("see End of loop ?" comment)
while True:
k1 = h * gf.f(t, y)
k2 = h * gf.f(t + (1/4) * h, y + (1/4) * k1)
k3 = h * gf.f(t + (3/8) * h, y + (3/32) * k1 + (9/32) * k2)
k4 = h * gf.f(t + (12/13) * h, y + (1932/2197) * k1 - (7200/2197) * k2 + (7296/2197) * k3)
k5 = h * gf.f(t + h, y + (493/216) * k1 - 8 * k2 + (3680/513) * k3 - (845/4104) * k4)
k6 = h * gf.f(t + (1/2) * h, y - (8/27) * k1 + 2 * k2 - (3544/2565) * k3 + (1859/4104) * k4 - (11/40) * k5)
er = (1/h) * ((1/360) * k1 - (128/4275) * k3 - (2197/7540) * k4 + (1/50) * k5 + (2/55) * k6)
# End of loop ?
if abs(er) <= TOL:
break
# update h before starting new iteration
h *= (TOL / (2 * abs(er))) ** (1 / 4)
# Continue
vect[i] = t = t0 + i * h
vectw[i] = y = y + ((16/135) * k1 + (6656/12825) * k3 + (28561/56430) * k4 - (9/50) * k5 + (2/55) * k6)
return vect, vectw
使用动态循环的想法是正确的,你不知道需要多少步才能通过积分区间的终点。
出于同样的原因,传递或定义 n
完全没有意义。
您可以扭曲步长自适应的逻辑。您不计算最优的 h
,您只是决定当前的 h
是否可以接受。在准备下一个方法步骤计算时,步长更新无论如何都会在每个循环结束时发生。但只有当本地 unit-step 误差小于公差时,您才会将计算出的 y
值附加到输出列表并提前时间。
缺少某些部分的骨骼可能看起来像这样
def RKF45(f,t,y,t_final,TOL):
T = [t]
Y = [y]
h = TOL**0.2
while t < t_final:
v4, v5, err = RKF45Step(f,t,y,h)
err = 1e-20+abs(err)
# scale the error by the expected solution magnitude
if 0.01*TOL < err < TOL:
t,y = t+h, y+h*v4
T.append(t)
Y.append(y)
q = 0.9*(TOL/err)**0.25
# min/max on q
q = max(0.8,min(1.5,q))
h = q*h
# min/max on h
return np.asarray(T), np.asarray(Y)
我目前正在将具有自适应步长的数值方法 RKF45 (Runge-Kutta-Fehlberg-45) 实施到 python 3 中,我相信我 运行 陷入了一个基本的循环问题我无法解决。请注意,我在实现该数值方法时遇到困难的部分是自适应步长。我了解如何实现这一点的基本算法,我将提供该算法,但首先让我们看一下我构建的执行 RF45 计算的函数:
def rkf45(n): # here we perform the necessary RKF45 computations
t0 = 0
t1 = 5 # this is just a label for the endpoint, not the i = 1 point.
y0 = 0
TOL = 5e-7
h = (t1 - t0) / n
vect = [0] * (n + 1)
vectw = [0] * (n + 1)
vect[0] = t = t0
vectw[0] = y = y0
for i in range(1, n + 1):
k1 = h * gf.f(t, y)
k2 = h * gf.f(t + (1/4) * h, y + (1/4) * k1)
k3 = h * gf.f(t + (3/8) * h, y + (3/32) * k1 + (9/32) * k2)
k4 = h * gf.f(t + (12/13) * h, y + (1932/2197) * k1 - (7200/2197) * k2 + (7296/2197) * k3)
k5 = h * gf.f(t + h, y + (493/216) * k1 - 8 * k2 + (3680/513) * k3 - (845/4104) * k4)
k6 = h * gf.f(t + (1/2) * h, y - (8/27) * k1 + 2 * k2 - (3544/2565) * k3 + (1859/4104) * k4 - (11/40) * k5)
er = (1/h) * ((1/360) * k1 - (128/4275) * k3 - (2197/7540) * k4 + (1/50) * k5 + (2/55) * k6)
# adaptive step size test goes here
vect[i] = t = t0 + i * h
vectw[i] = y = y + ((16/135) * k1 + (6656/12825) * k3 + (28561/56430) * k4 - (9/50) * k5 + (2/55) * k6)
return vect, vectw
请注意,gf.f
是我在一个单独的模块上定义的函数,该模块由:
def f(t, y):
a = -3 * t * y ** 2
b = 1 / (1 + t ** 3)
return a + b
现在,我评论 # adaptive step size goes here
的地方是我的问题所在:我需要测试是否 abs(er) > TOL
,如果这是真的,更新当前步长 h
h = h * q
其中 q = (TOL / (2 * abs(er))) ** (1 / 4)
和 使用此更新的步长重复 当前迭代,直到 abs(er) < TOL
。从那里开始,我需要在下一次迭代中使用这个更新的 h
。
我已经尝试使用 while
循环来实现这一点,但我肯定没有正确实现它;可能是因为我是新手,犯了一个愚蠢的错误。我还尝试使用 if
语句来测试是否 abs(er) > TOL
并从那里更新 h
但我不相信这会强制 for 循环使用更新的 h
.
如果我明白你需要什么:
def rkf45(n): # here we perform the necessary RKF45 computations
t0 = 0
t1 = 5 # this is just a label for the endpoint, not the i = 1 point.
y0 = 0
TOL = 5e-7
h = (t1 - t0) / n
vect = [0] * (n + 1)
vectw = [0] * (n + 1)
vect[0] = t = t0
vectw[0] = y = y0
for i in range(1, n + 1):
# Will loop until break ("see End of loop ?" comment)
while True:
k1 = h * gf.f(t, y)
k2 = h * gf.f(t + (1/4) * h, y + (1/4) * k1)
k3 = h * gf.f(t + (3/8) * h, y + (3/32) * k1 + (9/32) * k2)
k4 = h * gf.f(t + (12/13) * h, y + (1932/2197) * k1 - (7200/2197) * k2 + (7296/2197) * k3)
k5 = h * gf.f(t + h, y + (493/216) * k1 - 8 * k2 + (3680/513) * k3 - (845/4104) * k4)
k6 = h * gf.f(t + (1/2) * h, y - (8/27) * k1 + 2 * k2 - (3544/2565) * k3 + (1859/4104) * k4 - (11/40) * k5)
er = (1/h) * ((1/360) * k1 - (128/4275) * k3 - (2197/7540) * k4 + (1/50) * k5 + (2/55) * k6)
# End of loop ?
if abs(er) <= TOL:
break
# update h before starting new iteration
h *= (TOL / (2 * abs(er))) ** (1 / 4)
# Continue
vect[i] = t = t0 + i * h
vectw[i] = y = y + ((16/135) * k1 + (6656/12825) * k3 + (28561/56430) * k4 - (9/50) * k5 + (2/55) * k6)
return vect, vectw
使用动态循环的想法是正确的,你不知道需要多少步才能通过积分区间的终点。
出于同样的原因,传递或定义 n
完全没有意义。
您可以扭曲步长自适应的逻辑。您不计算最优的 h
,您只是决定当前的 h
是否可以接受。在准备下一个方法步骤计算时,步长更新无论如何都会在每个循环结束时发生。但只有当本地 unit-step 误差小于公差时,您才会将计算出的 y
值附加到输出列表并提前时间。
缺少某些部分的骨骼可能看起来像这样
def RKF45(f,t,y,t_final,TOL):
T = [t]
Y = [y]
h = TOL**0.2
while t < t_final:
v4, v5, err = RKF45Step(f,t,y,h)
err = 1e-20+abs(err)
# scale the error by the expected solution magnitude
if 0.01*TOL < err < TOL:
t,y = t+h, y+h*v4
T.append(t)
Y.append(y)
q = 0.9*(TOL/err)**0.25
# min/max on q
q = max(0.8,min(1.5,q))
h = q*h
# min/max on h
return np.asarray(T), np.asarray(Y)