我想在python中快速相乘两个多项式。由于我的多项式是相当大的(> 100000)元素,因此我必须将它们相乘很多。在下面,您将找到我的方法,
from numpy.random import seed, randint
from numpy import polymul, pad
from numpy.fft import fft, ifft
from timeit import default_timer as timer
length=100
def test_mul(arr_a,arr_b): #inbuilt python multiplication
c=polymul(arr_a,arr_b)
return c
def sb_mul(arr_a,arr_b): #my schoolbook multiplication
c=[0]*(len(arr_a) + len(arr_b) - 1 )
for i in range( len(arr_a) ):
for j in range( len(arr_b) ):
k=i+j
c[k]=c[k]+arr_a[i]*arr_b[j]
return c
def fft_test(arr_a,arr_b): #fft based polynomial multuplication
arr_a1=pad(arr_a,(0,length),'constant')
arr_b1=pad(arr_b,(0,length),'constant')
a_f=fft(arr_a1)
b_f=fft(arr_b1)
c_f=[0]*(2*length)
for i in range( len(a_f) ):
c_f[i]=a_f[i]*b_f[i]
return c_f
if __name__ == '__main__':
seed(int(timer()))
random=1
if(random==1):
x=randint(1,1000,length)
y=randint(1,1000,length)
else:
x=[1]*length
y=[1]*length
start=timer()
res=test_mul(x,y)
end=timer()
print("time for built in pol_mul", end-start)
start=timer()
res1=sb_mul(x,y)
end=timer()
print("time for schoolbook mult", end-start)
res2=fft_test(x,y)
print(res2)
#########check############
if( len(res)!=len(res1) ):
print("ERROR");
for i in range( len(res) ):
if( res[i]!=res1[i] ):
print("ERROR at pos ",i,"res[i]:",res[i],"res1[i]:",res1[i])
现在,这是我的详细方法, 1.首先,我尝试了天真地实现带有复杂度O(n ^ 2)的Schoolbook。但是正如您可能期望的那样,结果却非常缓慢。
第二,我在Numpy库中认识了polymul
。此功能比以前的功能快很多。但是我意识到这也是O(n ^ 2)的复杂性。您可以看到,如果增加长度k,则时间将增加k ^ 2倍。
我的第三种方法是使用内置FFT函数尝试基于FFT的乘法。我遵循了也描述了here的众所周知的方法,但是我无法使其正常工作。
现在我的问题是,
基于FFT的方法在哪里出错?你能告诉我如何解决吗?
我的观察到polymul
函数的O(n ^ 2)复杂度正确吗?
请问您是否有任何疑问。 预先感谢。
答案 0 :(得分:1)
- 基于FFT的方法在哪里出错?您能告诉我如何解决吗?
主要问题是,在基于FFT的方法中,您应该在乘法之后进行逆变换,但是代码中缺少该步骤。缺少这一步骤,您的代码应如下所示:
def fft_test(arr_a,arr_b): #fft based polynomial multiplication
arr_a1=pad(arr_a,(0,length),'constant')
arr_b1=pad(arr_b,(0,length),'constant')
a_f=fft(arr_a1)
b_f=fft(arr_b1)
c_f=[0]*(2*length)
for i in range( len(a_f) ):
c_f[i]=a_f[i]*b_f[i]
return ifft(c_f)
请注意,可能还有一些改进的机会:
a_f = fft(arr_a, length)
)传递来直接处理零填充numpy.multiply
处理。numpy.fft.rfft
和numpy.fft.irfft
(而不是numpy.fft.fft
和numpy.fft.ifft
)来提高性能。因此,用于实值输入的实现可能类似于:
from numpy.fft import rfft, rifft
from numpy import multiply
def fftrealpolymul(arr_a,arr_b): #fft based real-valued polynomial multiplication
L = len(arr_a1) + len(arr_b2) - 1
a_f=rfft(arr_a,L)
b_f=rfft(arr_b,L)
return irfft(multiply(a_f,b_f))
- 我是否发现
polymul
函数的O(n 2 )复杂度正确吗?
这似乎也是我观察到的性能,并且与numpy安装(版本1.15.4中的可用代码)相匹配,并且在最新的1.16.1版本中该部分似乎没有任何更改)。