简体   繁体   English

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

[英]FFT polynomial multiplication in Python using inbuilt Numpy.fft

I want to multiply two polynomials fast in python.我想在python中快速乘以两个多项式。 As my polynomials are rather large (> 100000) elements and I have to multiply lots of them.由于我的多项式相当大(> 100000)个元素,我必须将它们相乘。 Below, you will find my approach,下面,你会发现我的方法,

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])

Now, here are my approach in detail, 1. First, I tried myself with a naive implementation of Schoolbook with complexity O(n^2).现在,这里是我的详细方法, 1. 首先,我尝试了一个复杂度为 O(n^2) 的 Schoolbook 的幼稚实现。 But as you may expect it turned out to be very slow.但正如你所料,结果证明它非常慢。

  1. Second, I came to know polymul in the Numpy library.其次,我在Numpy库中认识了polymul This function is a lot faster than the previous one.这个功能比前一个快很多。 But I realized this is also a O(n^2) complexity.但我意识到这也是 O(n^2) 复杂度。 You can see, if you increase the length k the time increases by k^2 times.您可以看到,如果增加长度 k,时间会增加 k^2 倍。

  2. My third approach is to try a FFT based multiplication using the inbuilt FFT functions.我的第三种方法是使用内置的 FFT 函数尝试基于 FFT 的乘法。 I followed the the well known approach also described here but Iam not able to get it work.我遵循了这里也描述的众所周知的方法但我无法让它工作。

Now my questions are,现在我的问题是,

  1. Where am I going wrong in my FFT based approach?我的基于 FFT 的方法哪里出错了? Can you please tell me how can I fix it?你能告诉我我该如何解决吗?

  2. Is my observation that polymul function has O(n^2) complexity correct?我对polymul函数具有 O(n^2) 复杂度的观察是否正确?

Please, let me know if you have any question.如果您有任何问题,请告诉我。 Thanks in advance.提前致谢。

  1. Where am I going wrong in my FFT based approach?我的基于 FFT 的方法哪里出错了? Can you please tell me how can I fix it?你能告诉我我该如何解决吗?

The main problem is that in the FFT based approach, you should be taking the inverse transform after the multiplication, but that step is missing from your code.主要问题是,在基于 FFT 的方法中,您应该在乘法之后进行逆变换,但是您的代码中缺少该步骤。 With this missing step your code should look like the following:缺少此步骤后,您的代码应如下所示:

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)

Note that there may also a few opportunities for improvements:请注意,可能还有一些改进机会:

  • The zero padding can be handled directly by passing the required FFT length as the second argument (eg a_f = fft(arr_a, length) )可以通过将所需的 FFT 长度作为第二个参数传递来直接处理零填充(例如a_f = fft(arr_a, length)
  • The coefficient multiplication in your for loop may be directly handled by numpy.multiply . for 循环中的系数乘法可以直接由numpy.multiply处理。
  • If the polynomial coefficients are real-valued, then you can use numpy.fft.rfft and numpy.fft.irfft (instead of numpy.fft.fft and numpy.fft.ifft ) for some extra performance boost.如果多项式系数是实值的,那么您可以使用numpy.fft.rfftnumpy.fft.irfft (而不是numpy.fft.fftnumpy.fft.ifft )来获得一些额外的性能提升。

So an implementation for real-valued inputs may look like:因此,实值输入的实现可能如下所示:

from numpy.fft import rfft, irfft
def fftrealpolymul(arr_a, arr_b):  #fft based real-valued polynomial multiplication
    L = len(arr_a) + len(arr_b)
    a_f = rfft(arr_a, L)
    b_f = rfft(arr_b, L)
    return irfft(a_f * b_f)
  1. Is my observation that polymul function has O(n 2 ) complexity correct?我对polymul函数具有 O(n 2 ) 复杂度的观察是否正确?

That also seem to be the performance I am observing, and matches the available code in my numpy installation (version 1.15.4, and there doesn't seem any change in that part in the more recent 1.16.1 version).这似乎也是我观察到的性能,并且与我的 numpy 安装中的可用代码相匹配(版本 1.15.4,在最近的 1.16.1 版本中该部分似乎没有任何变化)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM