使用内置Numpy.fft在Python中进行FFT多项式乘法

时间:2019-05-26 08:37:22

标签: python fft multiplication polynomials

我想在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。但是正如您可能期望的那样,结果却非常缓慢。

  1. 第二,我在Numpy库中认识了polymul。此功能比以前的功能快很多。但是我意识到这也是O(n ^ 2)的复杂性。您可以看到,如果增加长度k,则时间将增加k ^ 2倍。

  2. 我的第三种方法是使用内置FFT函数尝试基于FFT的乘法。我遵循了也描述了here的众所周知的方法,但是我无法使其正常工作。

现在我的问题是,

  1. 基于FFT的方法在哪里出错?你能告诉我如何解决吗?

  2. 我的观察到polymul函数的O(n ^ 2)复杂度正确吗?

请问您是否有任何疑问。 预先感谢。

1 个答案:

答案 0 :(得分:1)

  
      
  1. 基于FFT的方法在哪里出错?您能告诉我如何解决吗?
  2.   

主要问题是,在基于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)

请注意,可能还有一些改进的机会:

  • 可以通过将所需的FFT长度作为第二个参数(例如a_f = fft(arr_a, length))传递来直接处理零填充
  • for循环中的系数乘法可以直接由numpy.multiply处理。
  • 如果多项式系数是实值,则可以使用numpy.fft.rfftnumpy.fft.irfft(而不是numpy.fft.fftnumpy.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))
  
      
  1. 我是否发现polymul函数的O(n 2 )复杂度正确吗?
  2.   

这似乎也是我观察到的性能,并且与numpy安装(版本1.15.4中的可用代码)相匹配,并且在最新的1.16.1版本中该部分似乎没有任何更改)。