多项式四阶牛顿迭代:9.33E inv 与 10.33E invsqrt

草稿。。。

自翻译了 David Harvey 的两篇论文(即 多项式牛顿迭代的分块优化) 之后,我就一直对 inv 9E 的实现好奇,它是 3 阶牛顿迭代,比其他 2 阶迭代复杂度低一截,在分块思路上也继续有效。

因此我就自然而然的产生了一个想法:是否存在 4 阶牛顿迭代呢?

经过一番思索,还真成功的推导出了 283\frac{28}{3} E 的逆和 313\frac{31}{3} E 的倒数平方根算法。

下面让我介绍这个算法。记号与上一篇博客相同,E(n)\mathsf{E}(n) 是一次长 nn 的 DFT/IDFT 运算。

倒数

假设我们已经获得了获得了 ff 的逆的前 nn 项,那么设:

g0=f1modxng_0 = f^{-1} \bmod {x^n}

想扩展到 4n4n 项,可以假设 gg 有如下形式

g=g0(1+c0xn+c1x2n+c2x3n)+O(x4n)g = g_0(1 + c_0 x^n + c_1x^{2n} + c_2x^{3n}) + O(x^{4n})

为了求解多项式 c0,c1,c2c_0,c_1,c_2,我们可以计算 fg0f g_0 的残差:

fg0=1+a0xn+a1x2n+a2x3n+O(x4n)fg_0 = 1 + a_0 x^n + a_1 x^{2n} + a_2 x^{3n} + O(x^{4n})

可得方程:

fg=(1+a0xn+a1x2n+a2x3n)(1+c0xn+c1x2n+c2x3n)+O(x4n)=1+O(x4n)fg = (1 + a_0 x^n + a_1 x^{2n} + a_2 x^{3n})(1 + c_0 x^n + c_1x^{2n} + c_2x^{3n}) + O(x^{4n})= 1 + O(x^{4n})

展开,对比系数解得

{a0+c0=0a1+c1+a0c0=0a2+a1c0+a0c1+c2=0{c0=a0c1=a02a1c2=a2+2a0a1a03\begin{cases} a_0 + c_0 = 0 \\ a_1+c_1+ a_0c_0 = 0 \\ a_2+a_1c_0+a_0c_1+c_2 = 0 \end{cases} \Rightarrow \begin{cases} c_0 = -a_0 \\ c_1 = a_0^2 - a_1 \\ c_2 = -a_2 + 2a_0a_1 - a_0^3 \end{cases}

需要注意的是这里 aia_i 是长为 nn 的多项式块,乘法会向下一块进位,所以其实还需要再取余。记

a02=q0+q1xna_0^2 = q_0 + q_1 x^n

带入得到,这就是我们要计算的式子了。

c0=a0,c1=q0a1,c2=q1a2+a0(2a1q0)modxn.\begin{aligned} \overline{c_0} &= -a_0,\\ \overline{c_1} &= q_0 - a_1,\\ \overline{c_2} &= q_1 - a_2 + a_0(2a_1 - q_0) \bmod x^n. \end{aligned}

计算过程也比较清晰:

  1. 已知 g0modxng_0 \bmod x^n
  2. 计算 fg0modx4nfg_0 \bmod x^{4n},,取出 a0,a1,a2a_0,a_1,a_2,开销 3E(4n)。
  3. 计算 a02=q0+q1xna_0^2 = q_0 + q_1x^n,取出 q0,q1q_0, q_1,开销 2E(2n)。
  4. 计算 a0(2a1q0)modxna_0(2a_1 - q_0) \bmod x^n,开销 2E(2n),这里复用上一步的 F2n(a0)\mathcal{F}_{2n}(a_0)
  5. 此时我们可以得到 c=c0+c1xn+c2x2nc = \overline{c_0} + \overline{c_1} x^n + \overline{c_2}x^{2n}
  6. 计算 g0cg_0 c,开销 2E(4n),这里复用了第一步的 F4n(g0)\mathcal{F}_{4n}(g_0)

总而言之,复杂度为:

T(n)=3E(4n)+2E(2n)+2E(2n)+2E(4n)=28E(n)T(n) = 3 \mathsf{E}(4n) + 2 \mathsf{E}(2n) + 2 \mathsf{E}(2n) + 2 \mathsf{E}(4n) = 28 \mathsf{E}(n)

因为每次 n4nn \to 4n,所以复杂度为

T(4n)=T(n)+28E(n),T(n)=283E(n)9.33E(n)T(4n) = T (n) + 28 \mathsf{E}(n), \quad T(n) = \frac{28}{3}\mathsf{E}(n) \approx 9.33 \mathsf{E}(n)

Taylor 展开

其实这个方程可以更快的求解,注意到:

C=cxn=fg01=a0xn+a1x2n+a2x3n(modx4n)C = cx^n = f g_0 - 1 = a_0x^n +a_1x^{2n}+a_2x^{3n} \pmod {x^{4n}}

然后考虑Taylor展开:

(1+C)1=1C+C2C3+O(x4n)=1a0xn+(a02a1)x2n+(2a0a1a03a2)x3n+O(x4n)\begin{aligned} (1+C)^{-1} &= 1 - C + C^2 - C^3 + O(x^{4n}) \\ &= 1 - a_0x^n + (a_0^2-a_1)x^{2n} + (2a_0a_1 - a_0^3-a_2) x^{3n} + O(x^{4n}) \end{aligned}

这样就可以直截了当地得到。

倒数平方根

道理相同,假设我们已经获得了获得了 ff 的倒数平方根的前 nn 项,那么设:

g0=f1/2modxng_0 = f^{-1/2} \bmod {x^n}

同样设

C=cxn=fg021=a0xn+a1x2n+a2x3n(modx4n)C = cx^n = f g_0^2 - 1 = a_0x^n +a_1x^{2n}+a_2x^{3n} \pmod {x^{4n}}

Taylor 展开得到

(1+C)1/2=112C+38C2516C3+O(x4n)=112(a0xn+a1x2n+a2x3n)+38(a02x2n+2a0a1x3n)516a03x3n+O(x4n).\begin{aligned} (1+C)^{-1/2} &= 1 - \frac12 C + \frac38 C^2 - \frac{5}{16}C^3 + O(x^{4n}) \\ &= 1 - \frac12(a_0x^n+a_1x^{2n}+a_2x^{3n}) \\ &\quad + \frac38(a_0^2x^{2n}+2a_0a_1x^{3n}) - \frac{5}{16}a_0^3x^{3n} + O(x^{4n}) . \end{aligned}

和倒数一样,aia_i 是多项式块,所以 a02a_0^2 的高半会进到下一块。仍然记

a02=q0+q1xn.a_0^2 = q_0 + q_1x^n.

那么修正因子

g=g0(1+c0xn+c1x2n+c2x3n)+O(x4n)g = g_0(1+c_0x^n+c_1x^{2n}+c_2x^{3n}) + O(x^{4n})

中的三个块为

c0=12a0,c1=12a1+38q0,c2=12a2+38q1+a0(34a1516q0)modxn.\begin{aligned} \overline{c_0} &= -\frac12 a_0,\\ \overline{c_1} &= -\frac12 a_1 + \frac38 q_0,\\ \overline{c_2} &= -\frac12 a_2 + \frac38 q_1 + a_0\left(\frac34a_1-\frac{5}{16}q_0\right) \bmod x^n. \end{aligned}

如果能直接算出 CC 的前三个块,那这里就结束了。问题在于倒数平方根的残差是 fg021fg_0^2-1,而 g02g_0^2 已经有 2n2n 项,ff4n4n 项,直接做普通卷积需要 8n8n,因此需要另想循环卷积的法子。

注意到 ffg02g_0^2 最多只有 6 项,开头一项还是 11,因此实际上只有 5 块。因此注意到 4n4n 的循环卷积可以写成:

fg02mod(x4n1)=1+a3+(a0+a4)xn+a1x2n+a2x3fg_0^2 \bmod (x^{4n} - 1) = 1 + a_3 + (a_0+a_4)x^n + a_1 x^{2n} + a_2x^{3}

接下来需要想办法把 a4a_4 抠出来。一个办法是再算一次点值

fg02mod(xnξ8)=f(ηx)g02(ηx)mod(xn1)=1+ξ8a0+ξ82a1+ξ83a2+ξ84a3+ξ85a4\begin{aligned} fg_0^2 \bmod (x^{n} - \xi_8) &= f(\eta x)g_0^2(\eta x) \bmod (x^n - 1) \\ &= 1 + \xi_8 a_0 + \xi_8^2a_1 + \xi_8^3a_2 + \xi_8^4a_3 + \xi_8^5a_4 \end{aligned}

其中 ξ8\xi_8 是 8 阶单位根,ξ84=1\xi_8^4 = -1,而 η=ξ8n\eta = \xi_{8n},也叫做 8n8n 阶单位根。由于 a1,a2,a3a_1,a_2,a_3 是无干扰的,我们可以计算得到 a0a_0 了。

这个 trick 和 inv 9E 所使用的 DFT(3n) 的 trick 本质相同,这里变成 DFT(4n) 了。

得到 a0,a1,a2a_0,a_1,a_2 后和前面的方法基本相同,就不再详述。具体计算流程如下:

  1. 已知 g0modxng_0 \bmod x^n
  2. 计算 fg02mod(x4n1)fg_0^2 \bmod (x^{4n}-1),开销 3E(4n)。
  3. 用长度 nn 的 twisted DFT 计算 xn=ηx^n=\eta 处的 fg02fg_0^2,开销 3E(n)。
  4. 恢复 a0,a1,a2a_0,a_1,a_2
  5. 计算 a02=q0+q1xna_0^2=q_0+q_1x^n,开销 2E(2n)。
  6. 计算 a0(34a1516q0)modxna_0(\frac34a_1-\frac{5}{16}q_0)\bmod x^n,开销 2E(2n),这里复用上一步的 F2n(a0)\mathcal{F}_{2n}(a_0)
  7. 拼出 c=c0+c1xn+c2x2nc=\overline{c_0}+\overline{c_1}x^n+\overline{c_2}x^{2n}
  8. 计算 g0cg_0c,开销 2E(4n),这里复用第一步的 F4n(g0)\mathcal{F}_{4n}(g_0)

于是单次 n4nn\to 4n 的开销为

3E(4n)+3E(n)+2E(2n)+2E(2n)+2E(4n)=31E(n).3\mathsf{E}(4n)+3\mathsf{E}(n)+2\mathsf{E}(2n)+2\mathsf{E}(2n)+2\mathsf{E}(4n)=31\mathsf{E}(n).

因此总复杂度为

T(4n)=T(n)+31E(n),T(n)=313E(n)10.33E(n).T(4n)=T(n)+31\mathsf{E}(n), \quad T(n)=\frac{31}{3}\mathsf{E}(n)\approx 10.33\mathsf{E}(n).

性能比较

取 NTT 模数 P=998244353P=998244353,并使用了 AVX2 来优化。

inv

算法1024 us/op4096 us/op8192 us/op65536 us/op524288 us/op
inv9e8.62638.09880.901780.2107359.195
inv9.33e8.14636.45978.166765.0667362.018
inv7.5e8.50345.36494.343884.2958058.103
inv12e11.19751.168110.1281069.60010351.450
inv10e8.39538.76282.788810.6737553.723

invsqrt

算法1024 us/op8192 us/op65536 us/op524288 us/op
invsqrt12e11.908116.2661114.25610902.573
invsqrt10.33e10.01292.285895.0628783.462

总结

本文通过尝试 4 阶牛顿迭代,得到了 9.33E 的多项式逆和 10.33E 的多项式倒数平方根算法。其中 inv 并没有在复杂度上取得突破,只是实机性能还不错。invsqrt 似乎并未查找到相关文献,可能这个算子不怎么受人关心,一般论坛上似乎都是使用 12E 的标准牛顿迭代,我这个思路可能是互联网上首次提出。

我还有一个 18E 的 pow 算法的 idea,等我尝试验证后再考虑把本文的两个算法实现交到 OJ 上测一下。