UOJ Logo negiizhao的博客

博客

<del>noip退役选手的一些扯淡</del>关于优化形式幂级数计算的 Newton 法的常数

2018-11-27 18:16:44 By negiizhao

非常抱歉……又让您失望了呢……

2 年过去了……我还是一点进步都没有……一次大型比赛都没打好……

真的会有那样一天……像您那样强吗?……


想写的那些东西果然还是太麻烦了……根本写不动……就写一些没什么意思的东西吧……关于优化求形式幂级数的牛顿法的常数。

虽然写得很糟糕但还算是能看的,所以就发出来了。

2020年疫情期间更新了一些东西


记号约定:

R[x] 表示系数属于交换环 R 的关于 x 的多项式环

R[[x]] 表示系数属于交换环 R 的关于 x 的形式幂级数环

deg(f)=max{n[xn]f0}

ord(f)=min{n[xn]f0}

Fn(f)fmod(xn1) 的长度为 n 的离散 Fourier 变换

ζnR 中的 n 次本原单位根

为了方便阅读,在指数部分会用 (f) 表示 i=1[xi1]fixi


以下是废话部分,可以跳过

对于给定的精度 n 和环 R=CZ/pZR[[x]] 中的形式幂级数 f 被表示为一个 R[x] 中的多项式 fmodxn

对于 f,gR[x] ,我们可以通过 3 次 FFT 和 n 次乘法用 O(nlogn) 时间计算出 fg。对于 f,gR[[x]] ,给定 fmodxngmodxn,我们可以用和多项式乘法几乎相同的时间计算出 fgmodxn=(fmodxn)(gmodxn)modxn

Newton 法

对于形式幂级数 fR[[x]],AR[[x]][[y]] ,满足 A(f)=0 ,令 f0=fmodxn ,那么有 fmodx2n=f0A(f0)A(f0)modx2n

这可以通过观察 Af0 的 Taylor 级数得到: 0=A(f)=A(f0)+A(f0)(ff0)11!+A(f0)(ff0)22!+

注意到 (ff0)2 最低非0项为 x2n 项,于是有 0=A(f0)+A(f0)(ff0)(modx2n)0=A(f0)A(f0)+(ff0)(modx2n)f=f0A(f0)A(f0)(modx2n)

Newton 法亦可表述为,对于形式幂级数 fR[[x]],AR[[x,y]] ,满足 A(x,f)=0 ,令 f0=fmodxn ,那么有 fmodx2n=f0A(x,f0)Ay(x,f0)modx2n

这个表述可能更容易被初学者接受。

观察式子可以发现式中 ord(A(f0))n,这也意味着计算中 A(f0) 精度只需达到 n(当然,类似『把 A(f0) 分成 2 部分相加,分别用不同精度的 A(f0) 计算』这样的做法是显然不可以的),后面将会看到这 2 条性质的重要性。


以下讨论每种操作的优化。对每种操作,内容分为三部分

  1. 各种操作直接按照式子计算所需要的时间。我们认为长度为 n 的 FFT 计算所需时间为 E(n),2 个精度为 n 的形式幂级数的乘法需要 M(n)=(3+o(1))E(2n)=(6+o(1))E(n) 时间。为了方便阅读,下文操作的时间中将会省略所有 o(1)
  2. 用循环卷积进行优化。注意到很多情况下,我们已经得到了结果中的一部分系数,而长度为 n 的 FFT 解决了循环卷积问题 fgmod(xn1),仅用于计算卷积会非常浪费。所以可以考虑先计算循环卷积,如果必要的话就进行一些处理,最后得到需要的系数。这里的时间以 2 个序列的循环卷积为单位计算,所以即使是平方也按照 3E 统计时间。在倒数部分,还会介绍这个技巧的一个特殊形式。
  3. 减少 FFT 次数。容易发现很多情况下多次计算了相同的 DFT,或是可以用线性变换的性质合并几次 IDFT,这一部分考虑减少这些额外的开销。

如果需要用到其他操作,我们不使用更高级部分的结果。

倒数

1

对于 fR[[x]],令 g=1/f,求 g

相当于 A(g)=fg1=0,令 g0=gmodxn ,考虑到分母精度只需达到 n,那么有 gmodx2n=g0fg01fmodx2n=g0(fg01)g0modx2n=2g0fg02modx2n

(好像能直接得到这个式子……)

gmodxn 计算 gmodx2n 需要 1 次长度为 2n 的乘法、1 次长度为 4n 的乘法,用时 18E(n)。所以计算 gmodxn 所需总时间为 18E(n)

2

对于 fR[[x]],令 g=1/f,求 g

考虑 gmodx2n=g0(fg01)g0modx2n,显然 deg((fmodx2n)g01)<3n ,而 ord((fmodx2n)g01)n,所以只需要计算 (fmodx2n)g0mod(x2n1) 即可。同样计算 (fg01)g0modx2n 也只需要长度为 2n 的循环卷积。用时 12E(n)。所以计算 gmodxn 所需总时间为 12E(n)

3

对于 fR[[x]],令 g=1/f,求 ggmodx2n=g0(fg01)g0modx2n

迭代中有两次和 g0 相关的长度为 2n 的循环卷积,可以记录下 F2n(g0) 而不是重新计算一遍。总时间为 10E(n)=123M(n)

如果允许长度为 3n 的 FFT,考虑 gmodx2n=g0(fg02g0)modx2n,用长度为 3n 的循环卷积计算 fg02 即可做到总时间 9E(n)=112M(n)。这个方法并没有使用特别的优化方法,但第 2 部分的模型并不支持快速计算 fg02,所以放在第 3 部分。

但我们仍然需要一个改进的做法,因为并不一定支持长度为 3n 的 FFT。另一方面需要注意的是,很多其他操作中包含倒数的迭代,每次迭代中都有其他地方(可能是下一次迭代中)需要使用 F2n(fmodx2n)F2n(g0),这时这个方法不比前一个做法更快。但是,只需稍加改进,这个做法即可在几乎所有情况下优于前一个做法。

我们使用长度为 3n 的 FFT 是为了计算长度为 3n 的循环卷积,而实际上并不是必须用循环卷积才可以解决问题。对 aR,a2n1,考虑在 R[x]/(x2n1)(xnan) 中计算卷积,即在 1,ζ2n,ζ2n2,,ζ2n2n1,a,aζn,aζn2,,aζnn1 上多点求值和插值。对 fR[x]/(x2n1)(xnan) 进行多点求值只需用 FFT 计算 F2n(f)Fn(f(ax)),而插值只需分别还原并 CRT 合并。

容易发现,如果在 R[x]/(x2n1)(xnan) 中进行卷积,仍然可以处理超出长度部分的影响。而这个做法不需要长度为 3n 的 FFT,同时也计算了 F2n(fmodx2n)F2n(g0),所需时间仍是改进前的 9E(n)=112M(n),所以这个做法可以几乎完全代替前一种做法。

我们可以把 F2n(f),Fn(f(ax)) 记为 F2n,n(f)。有些时候取 a=ζ4n 会比较方便,因为如果还需要计算长为 4n 的循环卷积,可以直接和 Fn(f(ζ4n3x)) 合并为 F4n(f),这将使实现变得简单一些。此时还可以直接计算 F4n(f) 进一步简化实现。

简单描述一下思路:为了算出所需结果 f,先算出 fmod(x2n1)(xnan),考虑超出部分对前n项(本应全是 0)的贡献,利用这些信息还原出这一部分,然后即可把这一部分对所需部分的影响消除掉。

算出结果需要在 1,ζ2n,ζ2n2,,ζ2n2n1,a,aζn,aζn2,,aζnn1 上多点求值和插值,多点求值即计算 F2n(f)Fn(f(ax)),插值即分别还原并 CRT 合并。

以上是一般情况的本质原理,实际实现可以不考虑这些,最后的推荐实现也可以这样描述。考虑将所需结果 f 表示为 axn+bx2n+cx3n,其中 a,b,cR[x]deg(a),deg(b),deg(c)<n,那么可以用循环卷积计算出 fmod(x2n1)fmod(xni)=f(ζ4nx)mod(xn1),也就相当于算出了 b,a+c,iabic,还原出 a 即可。

商数或对数

1

商数:对于 f,hR[[x]],令 g=1/f,q=hg=h/f,求 q

显然先求 g=1/f,再求 q=hg 即可。总时间为 24E(n)

对数:对于常数项为 1fR[[x]],令 g=logf=i=1(1)i1(f1)ii,求 g

这和下面的指数都要求 R 是特征为 0 且非零整数可逆的环,但有限精度时应该可以降低限制?

我们有 (logx)=(i=1(1x)ii)=i=0(1x)i=(1(1x))i=0(1x)i1(1x)=1x

所以 (logf)=flogf=f/f,于是求商数即可。总时间为 24E(n)

2

对于 f,hR[[x]],令 g=1/f,q=hg=h/f,求 q

直接求 g 再求卷积所需总时间为 18E(n)

如果不需要求 g,注意到 A(q)=fqh=0,令 g0=gmodxn,h0=hmodxn,q0=qmodxn=g0h0modxn,有 qmodx2n=q0fq0hfmodx2n=q0(fq0h)g0modx2n

  • 计算 g0 需要 12E(n) 时间
  • 计算 q0 需要 6E(n) 时间
  • 计算 (fq0h)g0 需要和倒数类似的 12E(n) 时间

所以计算 qmodx2n 总时间为 30E(n),计算 qmodxn 总时间为 15E(n)

3

对于 f,hR[[x]],令 g=1/f,q=hg=h/f,求 q

g0=gmodxn,g1=(gmodx2ng0)/xn,h0=hmodxn,h1=(hmodx2nh0)/xn

如果需要求 g,考虑计算 qmodx2n=(gmodx2n)(hmodx2n)modx2n=g0h0+(g0h1+g1h0)xnmodx2n

  • 计算 F2n(g0)g0,g1 需要 18E(n)
  • 计算 F2n(g1),F2n(h0),F2n(h1) 需要 6E(n) 时间
  • 计算 g0h0,g0h1+g1h0 需要 4E(n) 时间

所以计算 gmodx2n,qmodx2n 总时间为 28E(n),计算 gmodxn,qmodxn 总时间为 14E(n)=213M(n)

这里用到的技巧可以这样描述:对于 f,gR[[x]],已知 fmodxn,gmodxn,Fn(fmodxn/2),那么可以用 5E(n) 时间计算出 fgmodxn。这个技巧将在下文多次出现。

如果不需要求 g,仍然考虑 qmodx2n=q0(fq0h)g0modx2n

相比第 2 部分的算法,可以使用更快的计算倒数的方法。计算 q0 时使用的 F2n(g0) 可以保留用于计算 (fq01)g0。所以计算 qmodx2n 总时间为 24E(n),计算 qmodxn 总时间为 12E(n)=2M(n)

  • 计算 g0 需要 9E(n) 时间
  • 计算 F2n(g0),q0 需要 6E(n) 时间
  • 计算 fq0 需要 6E(n) 时间
  • 计算 (fq0h)g0,已知 F2n(g0),需要 4E(n) 时间

所以计算 qmodx2n 总时间为 25E(n),计算 qmodxn 总时间为 12.5E(n)=2112M(n)

观察 q0=g0h0modxn,(fq0h)g0,可以发现符合上文描述的技巧的使用条件,其中 (fq0h)g0 可视为计算 ((fq0h)/xn)g0,再考虑到相同的 DFT 只需计算一次,需要 9E(n) 时间计算,总时间为 12E(n)=2M(n)

平方根

1

对于 fR[[x]],令 g=f1/2,h=1/g=f1/2,求 g

相当于 A(g)=g2f=0,令 g0=gmodxn,h0=hmodxn ,那么有 gmodx2n=g0g02f2g0modx2n=g0(g02f)h02modx2n

gmodxn,hmodxn 计算 gmodx2n 需要 1 次长度为 2n 的乘法、1 次长度为 4n 的乘法,计算 hmodx2n 需要 1 次计算倒数的迭代,用时 36E(n)。所以计算 gmodxn,hmodxn 所需总时间为 36E(n) ,如果不需要计算 h,可以省略最后一次迭代中的相关计算,总时间为 27E(n)

2

对于 fR[[x]],令 g=f1/2,h=1/g=f1/2,求 g

gmodx2n=g0(g02f)h0/2modx2n 计算 g02 需要 1 次长度为 n 的循环卷积,计算 (g02f)h0 需要 1 次长度为 2n 的循环卷积,计算 hmodx2n 需要 1 次计算倒数的迭代,用时 21E(n)。所以计算 gmodxn,hmodxn 所需总时间为 21E(n) ,如果不需要计算 h,可以省略最后一次迭代中的相关计算,总时间为 15E(n)

3

对于 fR[[x]],令 g=f1/2,h=1/g=f1/2,求 g

gmodx2n=g0(g02f)h0/2modx2n

  • 保留前一轮迭代计算倒数时的 Fn(g0)
  • 计算 g02 需要 E(n) 时间
  • 计算 F2n(h0),(g02f)h0 需要 6E(n) 时间
  • 计算 F2n(gmodx2n),hmodx2n 需要 1 次计算倒数的迭代,已知 F2n(h0),需要 7E(n) 时间

所以计算 gmodxn,hmodxn 所需总时间为 14E(n),如果不需要计算 h,可以省略最后一次迭代中的相关计算,总时间为 10.5E(n)=134M(n)

此外,最后一轮中因为不需要进行计算倒数的迭代,所以不需要计算并保留 F2n(h0),观察 (g02f)h0,可以发现符合讨论商数时描述的技巧的使用条件,需要 5E(n) 计算,总时间为 10E(n)=123M(n)

另一个方法 是先考虑计算 h=1/g=f1/2,注意到 A(h)=fh21=0,有 hmodx2n=h0fh0212fh0modx2n=h0fh03h02modx2n 用长度为 4n 的循环卷积计算 fh03 需要 12E(n) 时间,所以求 h 的总时间为 12E(n)=2M(n)

仍然是考虑 gmodx2n=g0(g02f)h0/2modx2n

  • 计算 F2n(fmodxn),h0 需要 12E(n) 时间
  • 计算 F2n(h0),g0=fh0modxn 需要 4E(n) 时间
  • 计算 g02 需要 2E(n) 时间
  • 计算 (g02f)h0 需要 4E(n) 时间

所以计算 gmodx2n 总时间为 22E(n),计算 gmodxn 总时间为 11E(n)=156M(n)

指数

1

对于常数项为 0fR[[x]],令 g=expf=i=0fii!,h=1/g=1/expf,求 g

相当于 A(g)=loggf=0,令 g0=gmodxn ,那么有 gmodx2n=g0logg0f1/g0modx2n=g0(logg0f)g0modx2n=g0g0((g0/g0)f)modx2n

gmodxn,hmodxn 计算 gmodx2n 需要 1 次计算倒数的迭代,需要 1 次长度为 2n 的乘法、1 次长度为 4n 的乘法,计算 hmodx2n 需要 1 次计算倒数的迭代,用时 54E(n)。所以计算 gmodxn,hmodxn 所需总时间为 54E(n) ,如果不需要计算 h,可以省略最后一次迭代中的相关计算,总时间为 45E(n)

2

对于常数项为 0fR[[x]],令 g=expf,h=1/g=1/expf,求 g

首先需要改写迭代式,令 f0=fmodxn。注意到 ord(((g0/g0)f)g0)n,即 ord(g0/g0f)n1,那么有 g0h0(g0/g0f)=g0/g0f(modx2n1) gmodx2n=g0g0((g0/g0)f)modx2n=g0g0(g0/g0f)modx2n=g0g0(g0h0(g0/g0f))modx2n=g0g0(g0h0f(g0h01)f0)modx2n

再注意到 ord(g0h0f(g0h01)f0)=ord(g0/g0f)n1,左式中 ord(g0h01)n,所以 ord(g0h0f)n1

计算 g0h0g0h0f 需要 2 次长度为 n 的循环卷积,计算 (g0h01)f0g0(g0h0f(g0h01)f0) 需要 2 次长度为 2n 的循环卷积,所以从 gmodxn,hmodxn 计算 gmodx2n 用时 18E(n)。计算 hmodx2n 需要 1 次计算倒数的迭代,所以总时间为 30E(n)。如果不需要计算 h ,可以省略最后一次迭代中的相关计算,总时间为 24E(n)

3

对于常数项为 0fR[[x]],令 g=expf,h=1/g=1/expf,求 ggmodx2n=g0g0(g0h0f(g0h01)f0)modx2n

  • 保留前一轮迭代计算的 Fn(g0)
  • 计算 Fn(h0),F2n(h0) 需要 2E(n) 时间
  • 计算 g0h0 需要 E(n) 时间
  • 计算 g0h0mod(xn1) 需要 2E(n) 时间
  • 计算 F2n(g0),已知 Fn(g0),需要 E(n) 时间
  • 计算 (g0h01)f0mod(x2n1) 需要 6E(n) 时间
  • 计算 g0(g0h0f(g0h01)f0)mod(x2n1) 需要 4E(n) 时间
  • 计算 F2n(gmodx2n),hmodx2n 需要 1 次计算倒数的迭代,已知 F2n(h0),需要 8E(n) 时间

所以总时间为 24E(n)。如果不需要计算 h ,可以省略最后一次迭代中的相关计算,总时间为 20E(n)=313M(n)

因为写得比较早,所以似乎仍然可以继续进行不少优化……但就这样吧,因为还有更好的做法。

另一个方法 类似上面的改写,有 gmodx2n=g0g0((g0/g0)f)modx2n=g0g0((g0/g0f0+f0)f)modx2n=g0g0((g0h0(g0/g0f0)+f0)f)modx2n=g0g0((h0(g0g0f0)+f0)f)modx2n 其中 ord(g0g0f0)n,因为 (exp(f))=exp(f)f,那么有

  • 保留前一轮迭代计算的 Fn,n/2(g0)
  • 计算 g0f0 需要 2E(n) 时间
  • 计算 F2n(h0),h0(g0g0f0) 需要 6E(n) 时间
  • 计算 F2n(g0),已知 Fn,n/2(g0),需要 0.5E(n) 时间
  • 计算 g0((h0(g0g0f0)+f0)f) 需要 4E(n) 时间
  • 计算 F2n,n(g0modx2n),hmodx2n 需要 1 次计算倒数的迭代,已知 F2n(h0),需要 7E(n) 时间

所以总时间为 19.5E(n)。如果不需要计算 h ,可以省略最后一次迭代中的相关计算,总时间为 16E(n)=223M(n)

此外,最后一轮中因为不需要进行计算倒数的迭代,所以不需要计算并保留 F2n(h0),观察 h0(g0g0f0),可以发现符合讨论商数时描述的技巧的使用条件,所以可以用 5E(n) 时间计算,这样总时间为 15.5E(n)=2712M(n)


下面是发现循环卷积优化后的较重要的文章。除了以上内容以外,还有一类基于分块的优化,可以得到一些更好的结果。如果有兴趣的话,似乎可以在这些地方找到进一步优化的方法?

Removing redundancy in high-precision Newton iteration

Newton iteration revisited

Newton's method and FFT trading

Faster algorithms for the square root and reciprocal of power series

Faster exponentials of power series

A simple and fast algorithm for computing exponentials of power series

Fast algorithms for elementary operations on complex power series

A note on the fast power series' exponential

评论

神仙论文爷
你的 5.5 sqrt 呢
评论回复
negiizhao:改好了.jpg
OldDriverTree:回复 @negiizhao:什么东西啊orz
OldDriverTree:回复 @negiizhao:什么东西啊orz
orz论文哥
神仙论文哥,太强大了.jpg
神仙论文爷
和我的 log^2 跑的一样快,你还得加加油哦(
评论回复
rushcheyo:错号……
神仙论文爷

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。