多项式四阶牛顿迭代:9.33E inv 与 10.33E invsqrt
草稿。。。
自翻译了 David Harvey 的两篇论文(即 多项式牛顿迭代的分块优化) 之后,我就一直对 inv 9E 的实现好奇,它是 3 阶牛顿迭代,比其他 2 阶迭代复杂度低一截,在分块思路上也继续有效。
因此我就自然而然的产生了一个想法:是否存在 4 阶牛顿迭代呢?
经过一番思索,还真成功的推导出了 328 E 的逆和 331 E 的倒数平方根算法。
下面让我介绍这个算法。记号与上一篇博客相同,E(n) 是一次长 n 的 DFT/IDFT 运算。
倒数
假设我们已经获得了获得了 f 的逆的前 n 项,那么设:
g0=f−1modxn
想扩展到 4n 项,可以假设 g 有如下形式
g=g0(1+c0xn+c1x2n+c2x3n)+O(x4n)
为了求解多项式 c0,c1,c2,我们可以计算 fg0 的残差:
fg0=1+a0xn+a1x2n+a2x3n+O(x4n)
可得方程:
fg=(1+a0xn+a1x2n+a2x3n)(1+c0xn+c1x2n+c2x3n)+O(x4n)=1+O(x4n)
展开,对比系数解得
⎩⎨⎧a0+c0=0a1+c1+a0c0=0a2+a1c0+a0c1+c2=0⇒⎩⎨⎧c0=−a0c1=a02−a1c2=−a2+2a0a1−a03
需要注意的是这里 ai 是长为 n 的多项式块,乘法会向下一块进位,所以其实还需要再取余。记
a02=q0+q1xn
带入得到,这就是我们要计算的式子了。
c0c1c2=−a0,=q0−a1,=q1−a2+a0(2a1−q0)modxn.
计算过程也比较清晰:
- 已知 g0modxn。
- 计算 fg0modx4n,,取出 a0,a1,a2,开销 3E(4n)。
- 计算 a02=q0+q1xn,取出 q0,q1,开销 2E(2n)。
- 计算 a0(2a1−q0)modxn,开销 2E(2n),这里复用上一步的 F2n(a0)。
- 此时我们可以得到 c=c0+c1xn+c2x2n。
- 计算 g0c,开销 2E(4n),这里复用了第一步的 F4n(g0)。
总而言之,复杂度为:
T(n)=3E(4n)+2E(2n)+2E(2n)+2E(4n)=28E(n)
因为每次 n→4n,所以复杂度为
T(4n)=T(n)+28E(n),T(n)=328E(n)≈9.33E(n)
Taylor 展开
其实这个方程可以更快的求解,注意到:
C=cxn=fg0−1=a0xn+a1x2n+a2x3n(modx4n)
然后考虑Taylor展开:
(1+C)−1=1−C+C2−C3+O(x4n)=1−a0xn+(a02−a1)x2n+(2a0a1−a03−a2)x3n+O(x4n)
这样就可以直截了当地得到。
倒数平方根
道理相同,假设我们已经获得了获得了 f 的倒数平方根的前 n 项,那么设:
g0=f−1/2modxn
同样设
C=cxn=fg02−1=a0xn+a1x2n+a2x3n(modx4n)
Taylor 展开得到
(1+C)−1/2=1−21C+83C2−165C3+O(x4n)=1−21(a0xn+a1x2n+a2x3n)+83(a02x2n+2a0a1x3n)−165a03x3n+O(x4n).
和倒数一样,ai 是多项式块,所以 a02 的高半会进到下一块。仍然记
a02=q0+q1xn.
那么修正因子
g=g0(1+c0xn+c1x2n+c2x3n)+O(x4n)
中的三个块为
c0c1c2=−21a0,=−21a1+83q0,=−21a2+83q1+a0(43a1−165q0)modxn.
如果能直接算出 C 的前三个块,那这里就结束了。问题在于倒数平方根的残差是 fg02−1,而 g02 已经有 2n 项,f 又 4n 项,直接做普通卷积需要 8n,因此需要另想循环卷积的法子。
注意到 f 乘 g02 最多只有 6 项,开头一项还是 1,因此实际上只有 5 块。因此注意到 4n 的循环卷积可以写成:
fg02mod(x4n−1)=1+a3+(a0+a4)xn+a1x2n+a2x3
接下来需要想办法把 a4 抠出来。一个办法是再算一次点值
fg02mod(xn−ξ8)=f(ηx)g02(ηx)mod(xn−1)=1+ξ8a0+ξ82a1+ξ83a2+ξ84a3+ξ85a4
其中 ξ8 是 8 阶单位根,ξ84=−1,而 η=ξ8n,也叫做 8n 阶单位根。由于 a1,a2,a3 是无干扰的,我们可以计算得到 a0 了。
这个 trick 和 inv 9E 所使用的 DFT(3n) 的 trick 本质相同,这里变成 DFT(4n) 了。
得到 a0,a1,a2 后和前面的方法基本相同,就不再详述。具体计算流程如下:
- 已知 g0modxn。
- 计算 fg02mod(x4n−1),开销 3E(4n)。
- 用长度 n 的 twisted DFT 计算 xn=η 处的 fg02,开销 3E(n)。
- 恢复 a0,a1,a2。
- 计算 a02=q0+q1xn,开销 2E(2n)。
- 计算 a0(43a1−165q0)modxn,开销 2E(2n),这里复用上一步的 F2n(a0)。
- 拼出 c=c0+c1xn+c2x2n。
- 计算 g0c,开销 2E(4n),这里复用第一步的 F4n(g0)。
于是单次 n→4n 的开销为
3E(4n)+3E(n)+2E(2n)+2E(2n)+2E(4n)=31E(n).
因此总复杂度为
T(4n)=T(n)+31E(n),T(n)=331E(n)≈10.33E(n).
性能比较
取 NTT 模数 P=998244353,并使用了 AVX2 来优化。
inv
invsqrt
总结
本文通过尝试 4 阶牛顿迭代,得到了 9.33E 的多项式逆和 10.33E 的多项式倒数平方根算法。其中 inv 并没有在复杂度上取得突破,只是实机性能还不错。invsqrt 似乎并未查找到相关文献,可能这个算子不怎么受人关心,一般论坛上似乎都是使用 12E 的标准牛顿迭代,我这个思路可能是互联网上首次提出。
我还有一个 18E 的 pow 算法的 idea,等我尝试验证后再考虑把本文的两个算法实现交到 OJ 上测一下。