在数学中,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和math.SE中间。
答案 0 :(得分: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 ** 2
替换t*t
会有很好的改进,并允许近似略微优于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
快一个数量级。
答案 1 :(得分:1)
使用Python + Numpy,它可能在这里和那里进行了优化,因此不可能真正地对log(1+x)
vs x + 0.5 * x^2
进行基准测试。
所以我转到了C ++。
每次操作的时间与日志:19.57 ns
每次操作的时间顺序为2泰勒展开log:3.73 ns
大致是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);
}