在RK4算法中使用lambda函数

2024-10-17 06:15:15 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python-showhere中有两种实现经典Runge-Kutta格式的方法。第一个使用lambda函数,第二个没有lambda函数。在

哪一个会更快?为什么?


Tags: 方法lambda函数格式经典rungekuttashowhere
3条回答

@berna111和@matt2000都是正确的。lambda版本由于函数调用而产生额外的开销。尾部调用优化将尾部调用转换为while循环(即自动将lambda版本转换为while版本),消除了函数调用开销。在

请参阅https://stackoverflow.com/a/13592002/7421639,了解Python为什么不自动进行这种优化,而您必须使用像cocon这样的工具来进行预处理过程传递。在

我调整了给定链接中的代码,并使用cProfile来比较这两种技术:

import numpy as np
import cProfile as cP

def theory(t):
    return (t**2 + 4.)**2 / 16.

def f(x, y):
    return x * np.sqrt(y)

def RK4(f):
    return lambda t, y, dt: (
            lambda dy1: (
            lambda dy2: (
            lambda dy3: (
            lambda dy4: (dy1 + 2*dy2 + 2*dy3 + dy4)/6
                      )( dt * f( t + dt  , y + dy3   ) )
                      )( dt * f( t + dt/2, y + dy2/2 ) )
                      )( dt * f( t + dt/2, y + dy1/2 ) )
                      )( dt * f( t       , y         ) )


def test_RK4(dy=f, x0=0., y0=1., x1=10, n=10):
    vx = np.empty(n+1)
    vy = np.empty(n+1)
    dy = RK4(f=dy)
    dx = (x1 - x0) / float(n)
    vx[0] = x = x0
    vy[0] = y = y0
    i = 1
    while i <= n:
        vx[i], vy[i] = x + dx, y + dy(x, y, dx)
        x, y = vx[i], vy[i]
        i += 1
    return vx, vy


def rk4_step(dy, x, y, dx):
    k1 = dx * dy(x, y)
    k2 = dx * dy(x + 0.5 * dx, y + 0.5 * k1)
    k3 = dx * dy(x + 0.5 * dx, y + 0.5 * k2)
    k4 = dx * dy(x + dx, y + k3)
    return x + dx, y + (k1 + k2 + k2 + k3 + k3 + k4) / 6.


def test_rk4(dy=f, x0=0., y0=1., x1=10, n=10):
    vx = np.empty(n+1)
    vy = np.empty(n+1)
    dx = (x1 - x0) / float(n)
    vx[0] = x = x0
    vy[0] = y = y0
    i = 1
    while i <= n:
        vx[i], vy[i] = rk4_step(dy=dy, x=x, y=y, dx=dx)
        x, y = vx[i], vy[i]
        i += 1
    return vx, vy

cP.run("test_RK4(n=10000)")
cP.run("test_rk4(n=10000)")

得到了:

^{pr2}$

所以我想说,“lambda”实现中的function calloverhead使它变慢了。在

不过,请注意,我似乎在某种程度上失去了一些精确性,因为尽管结果彼此一致,但结果比示例中的结果更差:

>>> vx, vy = test_rk4()
>>> vy
array([   1.        ,    1.56110667,    3.99324757, ...,  288.78174798,
        451.27952013,  675.64427775])
>>> vx, vy = test_RK4()
>>> vy
array([   1.        ,    1.56110667,    3.99324757, ...,  288.78174798,
        451.27952013,  675.64427775])

如果使用实现尾部调用优化的Coconuttranspiler对代码进行预处理,则它们完全等效(与未处理的更快版本一样快),因此可以使用更方便的样式。在

# Save berna1111's code as rk4.coco; no modifications necessary.
$ coconut  target 3 rk4.coco & python3 rk4.py
         50007 function calls in 0.055 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.097    0.097 <string>:1(<module>)
    40000    0.038    0.000    0.038    0.000 rk4.py:243(f)
        1    0.000    0.000    0.000    0.000 rk4.py:246(RK4)
    10000    0.007    0.000    0.088    0.000 rk4.py:247(<lambda>)
        1    0.010    0.010    0.097    0.097 rk4.py:250(test_RK4)
        1    0.000    0.000    0.097    0.097 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method numpy.core.multiarray.empty}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


         50006 function calls in 0.057 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.057    0.057 <string>:1(<module>)
    40000    0.030    0.000    0.030    0.000 rk4.py:243(f)
    10000    0.019    0.000    0.049    0.000 rk4.py:265(rk4_step)
        1    0.007    0.007    0.057    0.057 rk4.py:273(test_rk4)
        1    0.000    0.000    0.057    0.057 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method numpy.core.multiarray.empty}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

相关问题 更多 >