多项式简明百科全书
、写在前面
为了方便本文的描述,定义如下内容:
我们说是一个次数界为(下文不严谨地简称为次)的多项式,是指是一个下标集合为的数组,并代表:
这个多项式。
我们用代表多项式在处的值。
、多项式乘法
利用就可以将多项式乘法的复杂度优化到。由于其讲解较为繁琐并且已逐渐普及~~(想必读者也不是来学这个的)~~,因此略过以及同样普及的。
至于,我们可以将系数拆成的形式缩小值域,将一个多项式拆为两个。做四次卷积以后,将得到的答案合并并取模即可。
当然还有利用模数计算再用中国剩余定理合并的做法,但第一种做法更加直观。
、分治
有两种分治,先来讲比较简单的第一种。
如果我们有一个长度为的数组,并且希望求:
直接按照暴力计算,是的(因为每一个只有两项,所以一次暴力乘法只有)。如果我们把乘法用来实现,我们就得到了的优秀复杂度。
怎么做才会更快呢?
分治往往能带来更加优秀的复杂度。直接利用之所以慢,是因为相乘的两个多项式次数相差太大,这会非常浪费。
那我们不妨考虑先计算左半部分和右半部分的积。这是两个次的多项式,直接用在的时间内相乘即可。那么我们的递归式就是:
解得,复杂度得到了很大优化。
那么再来讲讲的第二种分治。
你有一个下标集合为的数组,你想要求一个下标集合为的数组,满足:
边界条件。
看上去直接求好像有点困难,那么我们来考虑分治。我们把数组拆为前后两半,显然后一半对前一半是没有影响的,直接递归求解前一半。求解完毕以后我们来计算前一半对后一半的贡献,这时我们直接根据上面的式子即可,因为此时前后的影响已经被我们用分治消除了。最后我们递归继续后一半的求解。
综上所述,递归式为:
解得,复杂度较优秀。
第二个问题其实有复杂度更优秀的解法,可以参见下面的“多项式求逆”。但是分治的思想更加直观,并且有的类型并不能利用“多项式求逆”来做,因此分治的作用还是很大的。
、多项式倍增(思想)
倍增是接下来对许多多项式问题求解的重要方法。
在多项式中可能有一些我们要求的多项式次数是无限的,那么一般我们只需要知道它的前项,即的结果即可。直接求解或许也比较困难,此时可以考虑倍增。
我们先用简单的方法求出的平凡情况,然后就只需要考虑如何从
的结果推到的结果即可。一般来说,利用这个性质就可以得到很方便的求解方法。
、多项式求逆
多项式求逆是这样一个问题,已知一个次多项式,我们希望求一个次多项式,即:
显然要求的常数项不为。那么在意义下,直接求得。
现在我们考虑从的结果推出的结果。我们有:
把平方展开,得到:
这个式子好像有点,但是我们已经知道,因此我们对等式两边同时乘以,得到:
移项,得:
就可以直接进行倍增。递归式为,解得。
多项式求逆也是很多多项式问题的基础,在接下来的几种多项式黑科技中起到了很大的作用。
那么再来举一个应用多项式求逆的例子,就是在之前的“分治”里提到的第二个问题。为了保持完整我们再陈述一遍:
你有一个下标集合为的数组,你想要求一个下标集合为的数组,满足:
边界条件。
我们先想办法把上面的式子化成普通的卷积形式。我们把的枚举范围从改为卷积形式的,此时我们发现多计算了这一项。那么我们不妨令,我们就消去了这一项的贡献。同时注意到作为边界的如果直接用这个卷积形式计算会得到,那么我们可以强制将结果加。经过这番操作卷积的答案就为了。于是我们得到了非常优美的式子:
我们移项,得到:
利用多项式求逆求出,在两边同时乘上,于是得到了:
由于我们只要知道的前项,因此是没有关系的,于是我们就利用多项式求逆在的时间内解决了这个问题。
、多项式除法、取模
给你一个次多项式,以及一个次多项式(),我们希望求一个次多项式以及一个次多项式,满足:
那么我们称为除以的商,而称为除以的余数。
现在我们来考虑如何求和。我们先定义次多项式的一种变换:
不难看出即是将的系数反转一下。因此我们可以在的时间内完成这个变换。
那么我们来推式子,我们不妨用替换多项式内的,这样的结果显然依旧正确:
接着考虑在两边同乘,得到:
不难发现我们已经有意为那个变换做好了准备,那么我们将四个多项式都用变换来代替:
我们考虑消去最后一项,那么不妨把式子放到的意义下。此时最后一项肯定为,于是就消失了:
我们发现既然是次的,那么也是次的,对其没有影响。于是我们求的逆,就得到:
于是我们就求得了,然后简单地再做一次变换就得到了。此时算就很方便了,直接计算即可。
下面来讲一个多项式取模的应用——常系数齐次线性递推。
常系数齐次线性递推是指这样一个问题,给你一个数,接着对于,给出,代表递推的系数。再对于,给出作为初始值,对于,数列满足:
最后给你一个数,需要求出。的范围很大。
显然直接递推是不可行的,我们不妨把思路放的巧妙一些。我们设有一个奇怪的,其满足(有点难理解,你不妨假设只是一个代号,它根本不是一个数),那么它一定满足:
于是我们移项,得到:
我们将其当作一个次的多项式来看待,那么如果我们将对这个多项式取模,得到了商和余数。因为模多项式为,因此商乘以这个模多项式的答案没有贡献,我们只关心这个余数。显然余数是一个次多项式,你只需要知道即可得到答案。而数列的前项是给出的,因此代入计算就可以了。因此整个过程只需要一个意义为卷积的乘法,以及意义为多项式取模的取模的快速幂就可以在的时间内完成这个操作。
:更详细的证明可以看这里 (opens new window)。
、多项式求导、积分
这是两个很简单的变换,是为了下面的内容做好准备。
设是一个次多项式,那么的导数就是一个次多项式,并且满足:
直接计算即可。
设是一个次多项式,那么的积分就是一个次多项式,并且满足:
边界条件。
同样计算即可。
、多项式
事情开始变得诡异起来了。
多项式求是这样一个问题,给你一个次多项式,满足,你需要求一个次多项式,满足:
看到这个是不是一下子有点懵啊...没关系,我们来冷静分析。函数的一个特点就是,这就一下子好办了,我们把这个结论照搬到多项式上面去,利用复合函数的求导法则,对上面的等式两边求导,得到:
这是不是非常简单啊,直接多项式求逆就得到了,那么我们再将等式两边积分,就得到:
不过还有一个小疑问,求导一次再积分一次不是会丢掉常数项吗,这怎么办呢。其实没关系,我们已经保证,而,因此的常数项就是。于是我们就做完了。
、多项式牛顿迭代、泰勒展开(思想)
接下来我们讲一下多项式牛顿迭代和泰勒展开,为接下来的工作做准备。话说多项式真是强啊什么都能套上去。
先来讲牛顿迭代。
我们知道,如果我们希望在实数域内求得某个处处可导函数的零点的近似值,我们就可以用牛顿迭代来解决。我们随意选择一个初值,设其为,然后开始迭代。每一次从迭代到,我们的精度会得到很大提升,迭代式子为:
其实就相当于是在处的这条切线和横轴的交点,可以自己推一下式子证明。
我们将牛顿迭代应用到多项式上,具体来说:
我们给出一个次多项式以及一个以多项式为参数,并且结果也是一个多项式的函数,并希望计算出一个次多项式,满足:
把问题简单转化一下,就是希望求得的零点了。那么我们依然先求出意义下的简单结果,接着套用牛顿迭代的公式。设上一次迭代的答案为,我们就得到:
为什么求导的时候多项式会消失呢?这是因为是关于多项式的函数,其导数反映的是当多项式变化时的变化幅度,因此不变的多项式就被看作是常数项了。
同时我们可能还有一个疑问:牛顿迭代不是用来提高精度的吗?怎么在多项式上应用呢?我们感性理解一下,假设是意义下的结果,那么迭代式子显然可以得到一个次多项式,此时我们给出结论:通过这个牛顿迭代的式子,我们可以得到意义下的结果。对它的证明将放到泰勒展开的讲解内。
于是我们就可以发现,利用牛顿迭代公式,我们可以做到类似倍增的效果。那么许多看似复杂的问题就可以解决了。
现在来讲一下多项式的泰勒展开。
我们刚刚已经得到了牛顿迭代在多项式上的应用,但是对于它的正确性,我们却是一知半解。幸好我们还有一种可以较简单的进行证明的方法:多项式泰勒展开。同时利用泰勒展开我们还可以获得一些问题求解的思路。
我们已经知道泰勒展开式为:
我们可以证明其对以多项式为参数的函数也成立,这和一般情况是一样的。那么我们依然考虑之前牛顿迭代所讲的问题,我们以意义下的答案代入,求意义下的答案,那么我们有:
我们再次利用这个性质:,那么泰勒展开式中除了前两项以外都变成了,于是便不用再考虑后面了,得到:
而我们又有,于是左边就为,我们移项得:
两边同除:
再将加到右边:
我们此时发现这就是牛顿迭代的公式,于是就完成了对牛顿迭代正确性的证明。当然泰勒展开的作用不只是证明牛顿迭代,我们在做题时可能推出一个关于某个多项式的奇怪式子,如果你能发现它正好是一个常见函数的泰勒展开,就可以用专门求这个函数的方法来解决——例如我们即将要讲的。
、多项式
多项式求是这样一个问题,给你一个次多项式,满足,你需要求一个次多项式,满足:
由于我们已经有了关于的经验,看到这个就不要害怕了(都是纸老虎)。
但是直接利用求导积分的那一套似乎不可行,因为的导数依然是。但是我们正好可以利用求是简单的这一点,结合刚刚所讲的牛顿迭代来解决这个问题。
我们可以很快把转化为这样的问题:求一个次多项式,满足:
那么问题转化为求的零点,我们套用牛顿迭代的公式来倍增。因为的常数项一定是,因此的常数项就是,那么对于已知的意义下的答案,我们求意义下的答案:
而我们知道,因此:
就可以倍增求得答案了。递归式为,解得。
将与结合起来还可以得到多项式快速幂的算法,具体来说:
给出一个次多项式,以及次数,我们希望求:
直接利用快速幂来计算,每次乘法是的,一共次乘法,总复杂度为,因为,所以可以选择在每次乘法以后只保留前项。这是一个优秀的算法,但是下面的做法可以比这更优秀:
假设多项式的系数不为的最低次是,那么我们可以先计算,最后再简单地乘以即可,经过这样的处理,我们就保证了常数项为,就是可以计算的。
那么套用数域上的结论:,我们得到:
于是我们只要先求,再把每一项乘以,最后进行一次即可。如果之前有过处理,就像之前说的一样处理回去。这样的复杂度就是的。
、多项式开方
多项式开方是这样一个问题,给你一个次多项式,满足数域内存在某个数,使得,你需要求一个次多项式,满足:
头回生二回熟,我们立刻可以发现这也可以利用牛顿迭代来解决对吧。我们现在是要求的零点,那么时的答案就是的平方根(我们已保证了这一点可以做到),考虑用牛顿迭代公式进行倍增:
显然,那么把式子转化成好求的形式:
这样做的复杂度也是。
、多项式多点求值
事情好像更加毒瘤了。
我们可以利用在的时间内求出次多项式在个单位复数根处的值。那么我们自然还很好奇这样的一个问题:如果我随意给出个点,有什么快速的方法可以求得它的值吗?答案是肯定的,利用下面的方法,我们可以在的时间内求出一个次多项式在任意个点处的值。
首先给出一个简单的结论。回顾之前的多项式除法,对于次多项式,的值就是:
这个应该很好证明,在时值为,因此它的倍数在求时都为,都没有影响,就只剩下一个常数,那就是答案。
现在的问题,就转化成对于下标集合为的数组,我们希望快速求得的值,其中。
此时我们不妨考虑一下我们的老朋友分治。能否通过进行一些简单的处理,就能将变成两个次多项式递归求解呢?答案依然是肯定的。我们以左半部分为例,考虑将变成:
这样的答案是不变的,因为在等于左半部分任意的值时,后面的连乘结果为,没有影响,因此其倍数也不会有影响,而这式子是一个次的多项式,对其取模以后就得到了次的多项式。对于右半部分,显然也是一样的。
再考虑如何快速求那个连乘。显然这是一个分治可以解决的问题,我们不妨先用的时间预处理出每一层的连乘,接着再做一遍上述分治做法,我们在每一层的复杂度就是,解得。
、多项式多点插值
我们既然已经知道如何多点求值,自然也会好奇如何进行多点插值。这当然也是可以快速做到的,复杂度也是。不过其过程会更加复杂一些。
假设我们已经知道个互不相同的点值,那么由这个点插值出来的多项式一定是唯一的。那么我们不妨假设我们可以构造次多项式,满足当取时值为,取时值为,那么显然这个多项式就是:
现在来考虑怎么构造它,这其实非常简单,只需:
发现当取时分子必有一个为,因此值为。取时分母分子相互抵消,答案就为。于是我们有:
直接根据定义计算显然是的,我们来考虑一下怎么做会更快。
我们先来计算这一部分如何。我们考虑计算出次多项式,满足:
这可以用分治在的时间内解决。那么对于每一个,我们就是要求:
可是这个式子分子分母都是啊...这怎么办办...这时我们的救星出现了,它叫做洛必达法则:
对于函数,,如果当时,有,,那么有
这其实也不难理解,两个趋近于的数相除,我们用它们的变化率相除作为结果应该很自然。那么我们就可以利用洛必达法则,而,于是我们对于,要求的值就是。于是我们利用多项式多点求值,求出这个答案,这样也是的。
现在我们已经解决了第一部分,不妨用代替原来这一长串。于是我们现在要求的是:
又到我们的老朋友出场了,我们再次利用分治来解决问题。我们先递归求解左右两半的子问题,接着以左半部分为例,不难发现我们需要的贡献就是左半部分的答案,因为所有左半部分都是需要乘以它的。这又是一个分治的形式,于是先用的时间处理好,那么我们的每一层就是的。解得,我们终于解决了问题。
、多项式三角函数
毒瘤...
多项式三角函数是这样一个问题,给你一个次多项式,满足,你需要求两个次多项式和,满足:
和是最基本的三角函数,知道它们就可以知道所有三角函数。我们来考虑如何解决这个问题,从优美的欧拉公式入手:
我们将多项式代入公式中,得到:
那么我们可以将其代入复数域内计算,先将所有系数移到虚部再进行得到结果,将得到的结果取实部和虚部即可。因为,因此,可以进行。
虽然需要在复数域内计算,但是多项式三角函数应该也是可以取模的,我们考虑对实部和虚部的系数分别取模,把每次卷积运算拆做四次,再将贡献算入结果的实部或虚部即可。