2016-11-07 98 views
1

在数学中,Taylor series对于使用小函数多项式来近似函数是很重要的。使用泰勒级数来加速计算

我想知道这样的近似如何有用,例如为了加速计算。让我们用著名的泰勒级数:

log(1+x) = x + 0.5 * x^2 + (error term) 

道义上,计算等级2的多项式的值应比计算log快得多。

因此,一个码来测试此:

import numpy, time 

def f(t): 
    return t + 0.5 * t ** 2 
f = numpy.vectorize(f) 

s = time.time() 
for i in range(100): 
    x = numpy.random.rand(100000) 
    numpy.log(1 + x) 
print time.time() - s   # 0.556999921799 seconds 

s = time.time() 
for i in range(100): 
    x = numpy.random.rand(100000) 
    f(x) 
print time.time() - s   # arghh! 4.81500005722 seconds 

为什么是多项式方法比实际慢日志10次?我预计相反。

PS:这个问题可能是在SO和数学中间。

+0

你有没有一起来看看怎么单位计算日志numpy的?机会是numpy的log()本身已经非常优化 –

+4

使用'numpy.log(1 + x)'您使用的NumPy的数组编程以实际的矢量化方式运行,而np.vectorize作为[doc](https:/ /docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html)指出:“向量化函数主要是为了方便,而不是为了性能”。所以,等价的方法是直接使用'x':'x + 0.5 * x ** 2'代替'f(x)'。 – Divakar

+0

泰勒级数将会对远离扩展点的参数产生收敛问题。对于所有的推断都是如此。 – duffymo

回答

0

随着Python + Numpy,它可能在这里和那里优化,所以它是不可能真正基准log(1+x)x + 0.5 * x^2。 所以我转向了C++。

结果:每操作

时间与日志:19.57纳秒每个操作
时间与日志的顺序-2泰勒展开:3。73纳秒

所以大致x5因素!


#include <iostream> 
#include <math.h> 
#include <time.h> 
#define N (1000*1000*100) 
#define NANO (1000*1000*1000) 

int main() 
{ 
    float *x = (float*) malloc(N * sizeof(float)); 
    float y; 
    float elapsed1, elapsed2; 
    clock_t begin, end; 
    int i; 

    for (i = 0; i < N; i++) 
    x[i] = (float) (rand() + 1)/(float)(RAND_MAX); 

    begin = clock(); 
    for (i = 0; i < N; i++) 
    y = logf(x[i]); 
    end = clock(); 
    elapsed1 = float(end - begin)/CLOCKS_PER_SEC/N * NANO; 

    begin = clock(); 
    for (i = 0; i < N; i++) 
    y = x[i] + 0.5 * x[i] * x[i]; 
    end = clock(); 
    elapsed2 = float(end - begin)/CLOCKS_PER_SEC/N * NANO; 

    std::cout << "Time per operation with log: " << elapsed1 << " ns\n"; 
    std::cout << "Time per operation with order-2 Taylor epansion: " << elapsed2 << " ns"; 

    free(x); 

} 
+0

它可以在Python中进行基准测试,问题在于你将numpy的优化代码与原始python进行了比较。当比较基本的python和基本的python,或者numpy实现numpy时,近似的速度很明显。 – user2699

+0

确实如此,但比较“基本蟒蛇日志”和“基本蟒蛇泰勒展开”给出了您的“1.1818947792053223 /0.8402454853057861”的比例约为1.4。这是相当低的,我预计会有更多的x5或x10比例,因此C/C++版本可以看到更多的情况...... – Basj

+0

是的,解释器的开销并不太令人意外。 numpy版本显示0.07202601432800293 vs 0.0019881725311279297,这是你想表现的。 – user2699

1

使用numpy的矢量化操作几乎总是比你自己的代码中任何尝试的优化都快。正如@Divakar提到的那样,vectorize实际上只是一个编写for循环的简便方法,所以你的代码将比numpy的本地代码慢。

用标准Python代码替换numpy的优化例程,表明您的方法速度大致相同。

import math, numpy, time 


def f(t): 
    return t + 0.5 * t ** 2 

x = numpy.random.rand(1000000) 

s = time.time() 
for num in x: 
    math.log(1 + num) 
print (time.time() - s ) 

s = time.time() 
for num in x: 
    f(num) 
print (time.time() - s)  

结果:

1.1951053142547607 
1.3485901355743408 

逼近只是稍微慢一些,但幂是非常昂贵的。与t*t更换t ** 2给出了一个很好的改善,并允许近似略强于大盘python的log

1.1818947792053223 
0.8402454853057861 

编辑:另外,由于大课在这里进行了优化科学图书馆几乎任何一天胜过一周的手工编写的解决方案,这里是泰勒系列近似与numpy的矢量化操作,这是迄今为止最快的。请注意,唯一的重大变化是vectorize未在近似函数上调用,因此默认情况下使用numpy的矢量化操作。

import numpy, time 

def f(t): 
    return t + 0.5 * t ** 2 

x = numpy.random.rand(1000000) 
s = time.time() 
numpy.log(1 + x) 
print (time.time() - s) 

s = time.time() 
x = numpy.random.rand(100000) 
f(x) 
print (time.time() - s ) 

结果:

0.07202601432800293 
0.0019881725311279297 

有你有它,矢量化近似是在一个数量级的速度比numpy的真实矢量log

+0

嗯,最重要的是,numpy.log将在C中实现,而您的函数需要Python解释器。 – Max

+0

@Max,正好。当比较相等时(使用numpy的矢量化函数进行对数和近似),该函数要快得多。 – user2699

+0

非常有趣的阅读。结论:Numpy和Python太高级/太优化,无法测试2级泰勒展开的速度如何快于“log”。我会用纯C再次做,看看会发生什么。 – Basj