zoukankan      html  css  js  c++  java
  • 数学杂谈 #5

    多项式多点求值

    传统做法,原理是:

    [egin{aligned} x^n &equiv ax^{n-1}pmod {x-a}\ &equiv a^2x^{n-1}pmod {x-a}\ &dots\ &equiv a^npmod {x-a} end{aligned} ]

    所以对于要求值的多项式 (F(x)) 和给定的点 (x_1,x_2,dots x_m) ,我们就相当于求 (F(x)mod (x-x_1),dots,F(x)mod (x-x_m))

    直接求当然不行,但是如果我们求 (F(x)mod (x-a)) ,我们先将 (F(x)) 模上一个包含 ((x-a)) 因式的多项式 (P(x)) ,则 ((F(x)mod P(x))equiv F(x)pmod {x-a})

    这提示我们可以逐步降低 (F) 的次数来优化复杂度。采用分治策略,记 (P_{l,r}(x)=prod_{i=l}^r(x-x_i)) ,且 (M=lfloorfrac{l+r}2 floor) 。如果我们得到了 (F(x)mod P_{l,r}(x)) ,我们可以让余式分别模 (P_{l,M})(P_{M+1,r}) ,从而可以继续递归。

    (P) 需要最初分治求并且存下来。

    可以得到时间复杂度为 (O(mlog_2^2m)) ,由于频繁使用多项式取模,该算法拥有较大的常数。


    船新做法:[1]

    普通的和卷积为 (h_k=sum_{i+j=k}f_ig_j) ,我们用 (H=Fcdot G)(H(x)=F(x)cdot G(x)) 来表示。

    现在我们定义差卷积为 (h_k=sum_{i-j=k}f_ig_j) ,我们用 (H=F imes G)(H(x)=F(x) imes G(x)) 来表示[2]

    这个做法的原理是:

    [[x^0]F(x) imes frac{1}{1-ax}=F(a) ]

    自行展开即可得知。

    除此之外,差卷积还有重要的性质:

    [F imes (Gcdot H)=F imes G imes H ]

    按照定义展开并整理即可得证。

    因此,我们就相当于求 ([x^0]F(x) imes frac{1}{1-x_1x},dots,[x^0]F(x) imes frac{1}{1-x_mx})

    同样采取分治策略。记 (Q_{l,r}(x)=prod_{i=l}^r(1-x_ix)) ,且 (M=lfloorfrac{l+r}2 floor) 。如果我们得到了 (F(x) imes frac{1}{Q_{l,r}(x)}) ,我们就乘上 (Q_{M+1,r}(x)) 并进入左子区间递归;右子区间类似。

    由于我们最终只需要常数项的值,因此我们可以控制多项式长度为区间长度,这样就保证了时间复杂度。

    (Q) 同样需要最初分治求并且存下来。递归根部的 (F(x) imes frac{1}{Q_{1,m}(x)}) 需要求逆。

    那么时间复杂度为 (O(mlog_2^2m)) ,不过常数会小一些,并且也会好写一些。

    多项式快速插值

    传统做法,原理是拉格朗日插值:

    [f(x)=sum_{i=1}^nfrac{y_iprod_{j ot=i}(x-x_j)}{prod_{j ot=i}(x_i-x_j)} ]

    我们只需要快速展开它即可。

    将式子变形:

    [f(x)=sum_{i=1}^nleft(frac{y_i}{prod_{j ot=i}(x_i-x_j)}cdotprod_{j ot=i}(x-x_j) ight) ]

    首先处理 (prod_{j ot=i}(x_i-x_j)) 。设 (g(x)=prod_{i=1}^n(x-x_i)) ,则它相当于 (h_i(x)=frac{g(x)}{x-x_i})(x_i) 处的值。

    可以发现 (h_i(x))(x_i) 有定义,而 (frac{g(x_i)}{x-x_i}) 却没有定义。这就意味着后者的极限即为所需:

    [lim_{x ightarrow x_i}frac{g(x)}{x-x_i}=lim_{x ightarrow x_i}frac{g(x)-g(x_i)}{x-x_i}=g'(x_i) ]

    所以我们可以分治求出 (g(x)) 然后多点求值得到 (prod_{j ot=i}(x_i-x_j))

    接下来求解 (f(x)) 继续采用分治。设 (f_{l,r}(x))([l,r]) 的结果:

    [f_{l,r}(x)=sum_{i=l}^rleft(frac{y_i}{prod_{j ot=i}(x_i-x_j)}cdotprod_{lle jle r,j ot=i}(x-x_j) ight) ]

    设分治的划分点为 (M) ,那么可以得到:

    [f_{l,r}(x)=f_{l,M}(x)cdotprod_{i=M+1}^r(x-x_i)+f_{M+1,r}(x)prod_{i=l}^M(x-x_i) ]

    边界为 (f_{i,i}(x)=frac{y_i}{prod_{j ot=i}(x_i-x_j)})

    这个做法的复杂度是 (O(nlog_2^2n))常数也就见仁见智吧


    船新做法:还没有出现

    常系数齐次线性递推

    传统做法:

    我们可以很容易地构造出转移矩阵 (T)

    设初值的向量为 (old{A}) ,我们可以知道有:

    [a_n=(old{A} imes T^n)_0 ]

    根据递推式,当整数 (nge k) 时有:

    [(old{A} imes T^n)_0=sum_{i=1}^kf_i(old{A} imes T^{n-i})_0 ]

    尝试将 (()_0) 去掉,也就是要说明对于整数 (min[0,k)) 都有:

    [(old A imes T^n)_m=sum_{i=1}^kf_i(old A imes T^{n-i})_m ]

    显然有:

    [(old A imes T^n)_m=(old A imes T^{n+m})_0=a_{n+m} ]

    所以可以得到:

    [old A imes T^{n}=sum_{i=1}^kf_i imes old A imes T^{n-i} ]

    考虑 (old A) 不为 0 向量的时候,所以:

    [T^n=sum_{i=1}^kf_iT^{n-i} ]

    这是一个关于 (T) 的等式,也告诉我们 (T^n) 可以被表示为较低次项的多项式。所以我们可以对其最高次项频繁进行展开,直到多项式次数降为 (k-1) ;这个过程相当于是进行多项式取模:

    [T^nmod{left(T^k-sum_{i=0}^{k-1}f_{k-i}T^i ight)} ]

    因此,如果我们得到取模的系数,我们就可以将初值带入得到 (a_n) 的值。

    所以我们需要对多项式做快速幂,模式就为 (T^k-sum_{i=0}^{k-1}f_{k-i}T^i)

    时间复杂度是 (O(klog_2nlog_2k))

    背后的严格证明就略过吧


    船新做法:[3]

    常系数齐次线性递推有其一般形式。设 (A(x)) 为数列 ({a_n}) 的生成函数,则:

    [A(x)=frac{P(x)}{Q(x)} ]

    其中 (Q(x)) 与递推式 ({f_k}) 的生成函数 (F(x)) 相关,即 (Q(x)=1-F(x))(P(x))(A(x),Q(x)) 均相关。

    来源:

    (a_n) 的递推式改一改得到生成函数形式:

    [egin{aligned} a_nx^n&=sum_{i=1}^kf_ix^icdot a_{n-i}x^{n-i}\ Rightarrow A(x)&=F(x)A(x) end{aligned} ]

    但是这里的结果并不完整,因为当 (nge k) 的时候递推式才是有效的,当 (n<k) 的时候,需要进行一些修补。

    怎么修?很好办,减去不对的再加回对的就可以了,所以有:

    [P(x)equiv (1-F(x))A(x)pmod{x^{k}} ]

    根据这个形式我们可以得到什么?

    进行一些充满想象力的变换:

    [A(x)=frac{P(x)Q(-x)}{Q(x)Q(-x)} ]

    (V(x)=Q(x)Q(-x)) 。注意到 (V(x)=V(-x))所以 (V(x)) 没有奇数次项

    这意味着我们进行更深入的变化。设 (U(x^2)=V(x)) ,那么有:

    [A(x)=frac{G_0(x^2)}{U(x^2)}+xfrac{G_1(x^2)}{U(x^2)} ]

    其中 (G_0(x^2))(P(x)Q(-x)) 的偶数次项对应的多项式, (xG_1(x^2))(P(x)Q(-x)) 的奇数次项对应的多项式。

    由于我们只需要求 ([x^n]A(x)) ,所以我们可以舍弃一半的多项式,也就相当于是多项式长度不变,次数减半。最终我们只需要求某个分式的 ([x^0]) ,就可以直接计算了。

    这个算法的复杂度也是 (O(klog_2nlog_2k)) ,但是显然它会好写一些,常数也会小一些。

    据测试,除开各种复杂的优化,朴素的该算法的常数也仅有传统做法的 (frac 1 3) 左右。


    1. 参考来自 EI 的博客↩︎

    2. 这里我们不必考虑 (k<0) 的项,因此 (deg H=deg F)↩︎

    3. 参考来自 EI 的博客↩︎

  • 相关阅读:
    软件编写和设计中的18大原则
    Ubuntu CTRL+ALT+F1~F6 进入命令模式后不支持中文显示的解决办法
    BM串匹配算法
    KMP串匹配算法解析与优化
    mongodb随机查询一条记录的正确方法!
    这真的该用try-catch吗?
    计算机的本质与数值、文字、声音、图像
    编程语言的概念
    linux服务方式启动程序脚本(init.d脚本)
    linux的7种运行级别
  • 原文地址:https://www.cnblogs.com/crashed/p/14404356.html
Copyright © 2011-2022 走看看