zoukankan      html  css  js  c++  java
  • 快速求n阶多项式乘积

    设有两个n阶多项式

    A(n)=an-1x^n-1+an-2x^n-2+...+a0

    B(n)=bn-1x^n-1+bn-2x^n-2+...+b0

    则如何求A(n)与B(n)的乘积?

    通常的方法是

    C(n)的表达形式是

    C(n)=c(2n-2)x^(2n-2)+c(2n-1)x^(2n-1)+...+c0

    易知C(n)的第k项系数ck=sigma(ai*b(k-i))(0<=i<=k)

    很明显这个算法是Θ(n^2)

    所以我们从多项式的基本表达上去研究

    一个(n)阶多项式的表达方式有几种?

    最常见的方法就是上文的解析式,也就是系数表达式。f(x)=an-1x^n-1+an-2x^n-2+...+a0

    但是这种方法在乘法时的时间复杂度是n^2太慢了!

    另一种方法就是点值表达式,这种方法我们初中就学到过——在求解一次函数多项式的解析式(系数表达时),我们只需要找2个点就确定了其结构,在求解二次函数时也需三个点就能求出解析式,那么这个结论能否向高次方程扩张呢?

    答案是可以的

    猜想:对于n阶多项式,我们知道n个数对(xi,yi)且任意xi不相等,就能确定一个n阶多项式经过这些点

    证明:这是与线性代数相关的命题

    {1 x1 x1^2 x1^3... x1^n-1}    a0      y1

    {1 x2 x2^2 x2^3... x2^n-1}    a1      y2

    ......................................... *       =  

    .........................................

    {1 xn xn^2 xn^3... xn^n-1}  an        yn

    其行列式不为0(详见算导) 所以有且仅有一解;

    所以我们可以由一组n个元素的数对(xi,yi)求解出n阶多项式的系数表达。这称为插值。

    点对表达式的有点在于若两个n阶多项式相乘,如A(n)*B(n)

    设A(n)的点对表达为n个(Axi,Ayi)

    B(n)的点对表达为n个(Bxi,Byi)

    那么A()与B()的乘积的点值表示就是

    n个点(Axi*Bxi,Ayi*byi)

    这里确定一个2n的多项式仅用n个点是不够的

    所以上文中把A()和B()都得取2n个点对

    现在还有一个插值的逆操作——求值

    及知道一个n阶多项式的系数表达,如何在θ(n log n)的时间内求出n个元素的数对(xi,yi)?

    这里需要用到单位复数根的性质

    另单位复数(模长=1)e=cosα+isina;

    则e^2=cosα*cosα-sinα*sinα+(2cosα*sinα)i=cos2α+sin2αi

    及e的模长不变,角度转过一倍。

    则方程x^n=1的n解是什么

    由上述知解的模长必为1,切设其与x轴夹角为α,nα=2kπ。

    xi=sin(2πi/n)+cos(2πi/n)i

    x1..xn成为n阶的单位复数根

    于是我们可以取x1,x2..xn这几个单位复数根,再求出这几个点时的y值

    这个方法可以用递归进行,就是传说中的DFT啦

    f(xi)=an-1xi^n-1+an-2xi^n-2+...+a0

    定义F'(xi)=a0+a2xi+a4xi^2+...+an-2 x^(n/2-1)

    和 F"(xi)=a1+a3xi+a5xi^2+...+an-1 x(n/2-1)

    f(xi)=F'(xi^2)+xi*F"(xi^2)

    我们还发现其实对于x1..xn这个数在复数系中的位置的个数

    x1^2,x2^2..xn^2更少(再转了α后半圈xi与前半全重合)

    甚至有对于even(n),前一个的规模恰好是后一个的两倍

    现在我们要计算x1,x2..xn这n个点的平方在当前系数下的值(就变成了x2,x4..xn这n/2个点,数据规模缩小了一半)

    所以T(n)=2T(n/2)+θ(n)(对每个xi利用F'和F“”的值组合出f)

    根据主定理可知时间复杂度为n log n

    //见算导P533 这里求的是w0..wn-1,与上文中的1..n做出一点调整(wn=w0)还有不用记录当前传进去的几个单位复数根,因为单位复数根的个数与系数向量的模长相等,直接生成即可。
    FFT(a)// a is a vector including n elements
    {
      n=a.length();
      if(n==1) return a;
      wn=e(2πi/n);//就是最小转一次需要乘的值
      w=1;//方便线扫求出wi
      A0=(a0,a1,...,an-2);
      A1=(a1,a3,...,an-1);
      y0=FFT(A0);
      y1=FFT(A1);
      // y0,y1 is a vector including n/2 elements就是求出的F',F"啦
      for k=0 to n/2-1
        y[k]=y0[k]+wy1[k];
        y[k+(n/2)]=yk[0]-wy1[k];
        w=w*wn;
      return y;
    }
  • 相关阅读:
    第002篇 深入体验C#项目开发(一)
    C#编程打字指法练习
    第001篇——C#学习计划开启
    2020杭电多校第一场(待更新)
    LeetCode双周赛11
    LeetCode双周赛10
    LeetCode Weekly 156
    NOIP模板复习(4)区间操作之莫队算法,树状数组,线段树
    NOIP模板复习(3) 最短路三巨头Floyd,Dijkstra与SPFA
    NOIP模板复习(2) LCA的三种解法
  • 原文地址:https://www.cnblogs.com/dancer16/p/6937795.html
Copyright © 2011-2022 走看看