Commit bcee78cd authored by TrisolarisHD's avatar TrisolarisHD
Browse files

add poly/(inv)tri-function

parent be98fd0c
Loading
Loading
Loading
Loading

docs/math/poly-div-mod.md

deleted100755 → 0
+0 −130
Original line number Diff line number Diff line
## Description

给定多项式 $f\left(x\right),g\left(x\right)$,求 $g\left(x\right)$ 除 $f\left(x\right)$ 的商 $Q\left(x\right)$ 和余数 $R\left(x\right)$.

## Method

发现若能消除 $R\left(x\right)$ 的影响则可直接[**多项式求逆**](../poly-inv)解决.

考虑构造变换

$$f^{R}\left(x\right)=x^{\operatorname{deg}{f}}f\left(\frac{1}{x}\right)$$

观察可知其实质为反转 $f\left(x\right)$ 的系数.

设 $n=\operatorname{deg}{f},m=\operatorname{deg}{g}$.

将 $f\left(x\right)=Q\left(x\right)g\left(x\right)+R\left(x\right)$ 中的 $x$ 替换成 $\frac{1}{x}$ 并将其两边都乘上 $x^{n}$,得到:

$$\begin{aligned}
    f\left(\frac{1}{x}\right)&=x^{n-m}Q\left(x\right)x^{m}g\left(x\right)+x^{n-m+1}x^{m-1}R\left(x\right)\\
    f^{R}\left(x\right)&=Q^{R}\left(x\right)g^{R}\left(x\right)+x^{n-m+1}R^{R}\left(x\right)
\end{aligned}$$

注意到上式中 $R^{R}\left(x\right)$ 的系数为 $x^{n-m+1}$,则将其放到模 $x^{n-m+1}$ 意义下即可消除 $R^{R}\left(x\right)$ 带来的影响.

又因 $Q^{R}\left(x\right)$ 的次数为 $\left(n-m\right)<\left(n-m+1\right)$,故 $Q^{R}\left(x\right)$ 不会受到影响.

则:

$$f^{R}\left(x\right)\equiv Q^{R}\left(x\right)g^{R}\left(x\right)\pmod{x^{n-m+1}}$$

使用多项式求逆即可求出 $Q\left(x\right)$,将其反代即可得到 $R\left(x\right)$.

时间复杂度 $O\left(n\log{n}\right)$.

## Code

??? " `poly-div-mod.cpp` "

    ```cpp
    using Z=int;
    using mZ=long long;
    using poly_t=Z[mxdg];
    using poly=Z*const;

    inline int calcpw2(const int&n){
        int t=1;
        for(;t<n;t<<=1);
        return t;
    }
    inline void cp(const Z*const&sl,const Z*const&sr,Z*const&dl,Z*const&dr){
        std::copy(sl,sr,dl);
        if(sr-sl<dr-dl)
            std::fill(dl+(sr-sl),dr,0);
    }

    void polydiv(const poly&f,const int&n,const poly&g,const int&m,poly&Q,poly&R){
        static poly_t div_t;

        const int res=n-m+1;
        const int deg1=calcpw2(res+res);

        std::reverse_copy(g,g+m,Q);
        if(m<deg1)
            std::fill(Q+m,Q+deg1,0);
        polyinv(Q,div_t,res);

        std::reverse_copy(f+m-1,f+n,Q);
        std::fill(Q+res,Q+deg1,0);

        DFT(Q,deg1);DFT(div_t,deg1);
        for(int i=0;i!=deg1;++i)
            Q[i]=(mZ)Q[i]*div_t[i]%p;
        IDFT(Q,deg1);

        std::reverse(Q,Q+res);
        std::fill(Q+res,Q+deg1,0);

        const int deg2=calcpw2(n);

        cp(Q,Q+res,R,R+deg2);
        cp(g,g+m,div_t,div_t+deg2);

        DFT(R,deg2);DFT(div_t,deg2);
        for(int i=0;i!=deg2;++i)
            R[i]=(mZ)R[i]*div_t[i]%p;
        IDFT(R,deg2);

        for(int i=0;i!=m;++i)
            R[i]=sub(f[i],R[i]);
        std::fill(R+m-1,R+deg2,0);
    }

    void polymod(const poly&f,const int&n,const poly&g,const int&m,poly&R){
        static poly_t mod_t;

        const int res=n-m+1;
        const int deg1=calcpw2(res+res);

        std::reverse_copy(g,g+m,R);
        if(m<deg1)
            std::fill(R+m,R+deg1,0);
        polyinv(R,mod_t,res);

        std::reverse_copy(f+m-1,f+n,R);
        std::fill(R+res,R+deg1,0);

        DFT(R,deg1);DFT(mod_t,deg1);
        for(int i=0;i!=deg1;++i)
            R[i]=(mZ)R[i]*mod_t[i]%p;
        IDFT(R,deg1);

        std::reverse(R,R+res);
        std::fill(R+res,R+deg1,0);

        const int deg2=calcpw2(n);

        cp(g,g+m,mod_t,mod_t+deg2);

        DFT(R,deg2);DFT(mod_t,deg2);
        for(int i=0;i!=deg2;++i)
            R[i]=(mZ)R[i]*mod_t[i]%p;
        IDFT(R,deg2);

        for(int i=0;i!=m;++i)
            R[i]=sub(f[i],R[i]);
        std::fill(R+m-1,R+deg2,0);
    }
    ```
+34 −0
Original line number Diff line number Diff line
## Description

给定多项式 $f\left(x\right),g\left(x\right)$,求 $g\left(x\right)$ 除 $f\left(x\right)$ 的商 $Q\left(x\right)$ 和余数 $R\left(x\right)$。

## Method

发现若能消除 $R\left(x\right)$ 的影响则可直接[**多项式求逆**](../poly-inv)解决.

考虑构造变换

$$f^{R}\left(x\right)=x^{\operatorname{deg}{f}}f\left(\frac{1}{x}\right) $$

观察可知其实质为反转 $f\left(x\right)$ 的系数。

设 $n=\operatorname{deg}{f},m=\operatorname{deg}{g}$。

将 $f\left(x\right)=Q\left(x\right)g\left(x\right)+R\left(x\right)$ 中的 $x$ 替换成 $\frac{1}{x}$ 并将其两边都乘上 $x^{n}$,得到:

$$ \begin{aligned}
    f\left(\frac{1}{x}\right)&=x^{n-m}Q\left(x\right)x^{m}g\left(x\right)+x^{n-m+1}x^{m-1}R\left(x\right)\\
    f^{R}\left(x\right)&=Q^{R}\left(x\right)g^{R}\left(x\right)+x^{n-m+1}R^{R}\left(x\right)
\end{aligned} $$

注意到上式中 $R^{R}\left(x\right)$ 的系数为 $x^{n-m+1}$,则将其放到模 $x^{n-m+1}$ 意义下即可消除 $R^{R}\left(x\right)$ 带来的影响。

又因 $Q^{R}\left(x\right)$ 的次数为 $\left(n-m\right)<\left(n-m+1\right)$,故 $Q^{R}\left(x\right)$ 不会受到影响。

则:

$$f^{R}\left(x\right)\equiv Q^{R}\left(x\right)g^{R}\left(x\right)\pmod{x^{n-m+1}}$$

使用多项式求逆即可求出 $Q\left(x\right)$,将其反代即可得到 $R\left(x\right)$。

时间复杂度 $O\left(n\log{n}\right)$。
+24 −24
Original line number Diff line number Diff line
(本页面部分内容转载自[桃酱的算法笔记](https://zhuanlan.zhihu.com/c_1005817911142838272),原文戳[链接](https://zhuanlan.zhihu.com/p/41867199),已获得作者授权)

一直想学 FFT,之前牛客的多有一道组合数学就用 FFT 写的,而且当时还傻乎乎的用唯一分解定理,但是自己好久没静下心学什么了,而且自己的数学功底又不好,导致一直学不会。看了很多人的博客也没看明白,尤其是原根。在我看了几十篇博客之后终于看懂了……所以想写一篇能够让大多数人都看得懂的教程。花费时间 3 天终于写完啦~~~~\~~
一直想学 FFT,之前牛客的多有一道组合数学就用 FFT 写的,而且当时还傻乎乎的用唯一分解定理,但是自己好久没静下心学什么了,而且自己的数学功底又不好,导致一直学不会。看了很多人的博客也没看明白,尤其是原根。在我看了几十篇博客之后终于看懂了……所以想写一篇能够让大多数人都看得懂的教程。花费时间 3 天终于写完啦~~~~\~~

另外,本文 FFT 部分的代码实现全部参考 kuangbin 的模板(2018.7 更新)资源地址如下

@@ -100,7 +100,7 @@ $$

。另外, $i$ 对于虚数的意义,与 $1$ 对于实数的意义是一样的。如果我说得不够明确,你可以看下面我引用的百科说明。

> 在数学中,虚数就是形如 $a+b \times i$ 的数,其中 $a,b$ 是实数,且 $b \neq 0$ , $i^2 = - 1$ 。虚数这个名词是 17 世纪著名数学家笛卡尔创立,因为当时的观念认为这是真实不存在的数字。后来发现虚数 $a+b \times i$ 的实部 $a$ 可对应平面上的横轴,虚部 $b$ 与对应平面上的纵轴,这样虚数 $a+b \times i$ 可与平面内的点 $(a,b)$ 对应。
> 在数学中,虚数就是形如 $a+b \times i$ 的数,其中 $a,b$ 是实数,且 $b \neq 0$  $i^2 = - 1$ 。虚数这个名词是 17 世纪著名数学家笛卡尔创立,因为当时的观念认为这是真实不存在的数字。后来发现虚数 $a+b \times i$ 的实部 $a$ 可对应平面上的横轴,虚部 $b$ 与对应平面上的纵轴,这样虚数 $a+b \times i$ 可与平面内的点 $(a,b)$ 对应。
>
> 可以将虚数 $bi$ 添加到实数 $a$ 以形成形式 $a + bi$ 的复数,其中实数 $a$ 和 $b$ 分别被称为复数的实部和虚部。一些作者使用术语纯虚数来表示所谓的虚数,虚数表示具有非零虚部的任何复数。——百度百科

@@ -160,7 +160,7 @@ $$

(因为他的角度是相当于单位角度),就能知道 $\omega_4^0, \omega_4^1, \omega_4^2, \omega_4^3$ 了呢?当然是这样的……

 $\omega_4^0$ 恒等于 $1$ , $\omega_4^2$ 的角度是 $\omega_4^0$ 的两倍,所以 $\omega_4^2 = (\omega_4^1)^2 = i^2=-1$ , 依次以此类推。
 $\omega_4^0$ 恒等于 $1$ , $\omega_4^2$ 的角度是 $\omega_4^0$ 的两倍,所以 $\omega_4^2 = (\omega_4^1)^2 = i^2=-1$  依次以此类推。

因此,我们只要知道 $\omega_k^1$ ,就能求出 $\omega_k^n$ 。所以我们把 $\omega_k^1$ 称为单位复根,简写为 $\omega_k$

@@ -216,13 +216,13 @@ $$

!前方高能:

这个函数能处理的多项式长度只能是 $2^m(m \in N^ \times )$ , 否则在分治的时候左右不一样长,右边取不到系数了,程序没法进行。所以要在第一次 DFT 之前就把序列向上补成长度为 $2^m(m \in N^ \times )$ (高次系数补 $0$ )、最高项次数为 $n-1$ 的多项式。一定要预处理哦
这个函数能处理的多项式长度只能是 $2^m(m \in N^ \times )$  否则在分治的时候左右不一样长,右边取不到系数了,程序没法进行。所以要在第一次 DFT 之前就把序列向上补成长度为 $2^m(m \in N^ \times )$ (高次系数补 $0$ )、最高项次数为 $n-1$ 的多项式。一定要预处理哦

然后我在代入值的时候,因为要代入 $n$ 个不同值,所以我们就代入 $\omega_n^0,\omega_n^1,\omega_n^2,\cdots, \omega_n^{n-1} (n=2^m(m \in N^ \times ))$

一共 $2^m$ 个不同值。

```c++
```cpp
/*
 * 做 FFT
 *len 必须是 2^k 形式
@@ -260,7 +260,7 @@ void fft(Complex y[], int len, int on) {

这里附上代码

```c++
```cpp
/*
 * 进行 FFT 和 IFFT 前的反置变换
 * 位置 i 和 i 的二进制反转后的位置互换
@@ -333,7 +333,7 @@ $$

记 $S\left(\omega_n^a\right)=\sum_{i=0}^{n-1}\left(\omega_n^a\right)^i$。

当 $a=0$ 时,$S\left(\omega_n^a\right)=n$.
当 $a=0$ 时,$S\left(\omega_n^a\right)=n$

当 $a\neq 0$ 时,我们错位相减一下

@@ -371,7 +371,7 @@ $$

所以我们 fft 函数可以集 DFT 和 IDFT 于一身。见下

```c++
```cpp
/*
 * 做 FFT
 *len 必须是 2^k 形式
@@ -405,7 +405,7 @@ void fft(Complex y[], int len, int on) {

好了现在附上全部代码([HDU 1402](http://acm.hdu.edu.cn/showproblem.php?pid=1402)),序言说过代码来自 kuangbin 的模板~~~~~

```c++
```cpp
#include <cmath>
#include <cstdio>
#include <cstring>
+11 −15
Original line number Diff line number Diff line
@@ -6,23 +6,19 @@

其实这个变换在信号处理中应用很广泛,fft 是 double 类型的,但是 walsh 把信号在不同震荡频率方波下拆解,因此所有的系数都是绝对值大小相同的整数,这使得不需要作浮点数的乘法运算,提高了运算速度。

所以,FWT 和 FFT 的核心思想应该是相同的都是对数组的变换。我们数组 $A$ 经过快速沃尔什变换之后记作 $FWT[A]$ .
所以,FWT 和 FFT 的核心思想应该是相同的都是对数组的变换。我们记对数组 $A$ 进行快速沃尔什变换后得到的结果为 $FWT[A]$ 

那么 FWT 核心思想就是:

我们需要一个新序列 $C$ ,由序列 $A$ 和序列 $B$ 经过某运算规则得到,即 $C = A \cdot B$ 
我们需要一个新序列 $C$ ,由序列 $A$ 和序列 $B$ 经过某运算规则得到,即 $C = A \cdot B$ 

我们先正向得到 $FWT[A], FWT[B]$ 
我们先正向得到 $FWT[A], FWT[B]$,再根据 $FWT[C]=FWT[A] \cdot FWT[B]$ 在 $O(n)$ 的时间复杂度内求出 $FWT[C]$ ;

然后根据 $FWT[C]=FWT[A] \cdot FWT[B]$ 

在 $O(n)$ 求出 $FWT[C]$ 

然后逆向想运算得到原序列 $C$ 。时间复杂度为 $O(nlogn)$ 
然后逆向运算得到原序列 $C$ 。时间复杂度为 $O(n \log{n})$。

在算法竞赛中,FWT 是用于解决对下标进行位运算卷积问题的方法。

公式: $C[i] = \sum_{i=j \bigoplus k}A[j] * B[k]$ 
公式: $C_{i} = \sum_{i=j \bigoplus k}A_{j} B_{k}$

(其中 $\bigoplus$ 是二元位运算中的某一种, $*$ 是普通乘法)

@@ -38,9 +34,9 @@

现在要得到 $FWT[C] = FWT[A] * FWT[B]$ ,我们就要构造这个 fwt 的规则。

我们按照定义,显然可以构造 $FWT[A] = A' = \sum_{i=i|j}A[j]$ ,来表示 $j$ 满足二进制中 $1$ 为 $i$ 的子集。
我们按照定义,显然可以构造 $FWT[A] = A' = \sum_{i=i|j}A_{j}$ ,来表示 $j$ 满足二进制中 $1$ 为 $i$ 的子集。

那么显然会有 $C[i] = \sum_{i=j|k}A[j]*B[k] \Rightarrow FWT[C] = FWT[A] * FWT[B]$ 
那么显然会有 $C_{i} = \sum_{i=j|k}A_{j}*B_{k} \Rightarrow FWT[C] = FWT[A] * FWT[B]$

那么我们接下来看 $FWT[A]$ 怎么求。

@@ -56,7 +52,7 @@ $$

其中 merge 表示像字符串拼接一样把它们拼起来, $+$ 就是普通加法,表示对应二进制位相加。

这样我们就通过二分能在 $O(logn)$ 完成拼接,每次拼接的时候要完成一次运算,也就是说在 $O(nlogn)$ 的时间复杂度得到了 $FWT[A]$ 。
这样我们就通过二分能在 $O(\log{n})$ 的时间复杂度内完成拼接,每次拼接的时候要完成一次运算,也就是说在 $O(n\log{n})$ 的时间复杂度得到了 $FWT[A]$ 。

接下来就是反演了,其实反演是很简单的,既然知道了 $A_0$ 的本身的子集是他自己 ( $A_0 = FAT[A_0]$ ), $A_1$ 的子集是 $FAT[A_0] + FAT[A_1](A_1'= A_0' + A_1'$ ),那就很简单的得出反演的递推式了:

@@ -88,7 +84,7 @@ $$

公式如下:

 $A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ( $C_1$ 表示 $i \And j$ 奇偶性为 $0$ , $C_2$ 表示 $i \And j$ 的奇偶性为 $1$ )
 $A_{i} = \sum_{C_1}A_{j} - \sum_{C_2}A_{j}$ ( $C_1$ 表示 $i \And j$ 奇偶性为 $0$ , $C_2$ 表示 $i \And j$ 的奇偶性为 $1$ )

结论:

@@ -104,7 +100,7 @@ $$

类比异或运算给出公式:

 $A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ( $C_1$ 表示 $i|j$ 奇偶性为 $0$ , $C_2$ 表示 $i|j$ 的奇偶性为 $1$ )
 $A_{i} = \sum_{C_1}A_{j} - \sum_{C_2}A_{j}$ ( $C_1$ 表示 $i|j$ 奇偶性为 $0$ , $C_2$ 表示 $i|j$ 的奇偶性为 $1$ )

$$
FWT[A] = merge(FWT[A_1] - FWT[A_0], FWT[A_1] + FWT[A_0])
+13 −14
Original line number Diff line number Diff line
@@ -2,22 +2,22 @@

### 多项式的度

对于一个多项式 $f\left(x\right)$,称其最高次项的次数为该多项式的**度(Degree)**,记作 $\operatorname{deg}{f}$.
对于一个多项式 $f\left(x\right)$称其最高次项的次数为该多项式的**度(Degree)**记作 $\operatorname{deg}{f}$

### 多项式的逆元

对于多项式 $f\left(x\right)$,若存在 $g\left(x\right)$ 满足:
对于多项式 $f\left(x\right)$若存在 $g\left(x\right)$ 满足

$$ \begin{aligned}
    f\left(x\right)g\left(x\right)&\equiv 1\pmod{x^{n}}\\
    \operatorname{deg}{g}&\leqslant\operatorname{deg}{f}
\end{aligned} $$

则称 $g\left(x\right)$ 为 $f\left(x\right)$ 在模 $x^{n}$ 意义下的**逆元(Inverse Element)**,记作 $f^{-1}\left(x\right)$.
则称 $g\left(x\right)$ 为 $f\left(x\right)$ 在模 $x^{n}$ 意义下的**逆元(Inverse Element)**记作 $f^{-1}\left(x\right)$

### 多项式的余数和商

对于多项式 $f\left(x\right),g\left(x\right)$,存在**唯一**的 $Q\left(x\right),R\left(x\right)$ 满足:
对于多项式 $f\left(x\right),g\left(x\right)$存在**唯一**的 $Q\left(x\right),R\left(x\right)$ 满足

$$ \begin{aligned}
    f\left(x\right)&=Q\left(x\right)g\left(x\right)+R\left(x\right)\\
@@ -33,7 +33,7 @@ $$f\left(x\right)\equiv R\left(x\right)\pmod{g\left(x\right)}$$

### <span id="ln-exp">多项式的对数函数与指数函数</span>

对于一个多项式 $f\left(x\right)$,可以将其对数函数看作其与麦克劳林级数的复合:
对于一个多项式 $f\left(x\right)$可以将其对数函数看作其与麦克劳林级数的复合:

$$\ln{\left(1-f\left(x\right)\right)}=-\sum_{i=1}^{+\infty}\frac{f^{i}\left(x\right)}{i}$$

@@ -43,7 +43,7 @@ $$\exp{f\left(x\right)}=e^{f\left(x\right)}=\sum_{i=0}^{+\infty}\frac{f^{i}\left

### 多项式的多点求值和插值

**多项式的多点求值(Multi-point evaluation)** 即给出一个多项式 $f\left(x\right)$ 和 $n$ 个点 $x_{1},x_{2},...,x_{n}$,
**多项式的多点求值(Multi-point evaluation)** 即给出一个多项式 $f\left(x\right)$ 和 $n$ 个点 $x_{1},x_{2},...,x_{n}$

$$f\left(x_{1}\right),f\left(x_{2}\right),...,f\left(x_{n}\right) $$

@@ -53,11 +53,10 @@ $$\left(x_{0},y_{0}\right),\left(x_{1},y_{1}\right),...,\left(x_{n},y_{n}\right)

求一个 $n$ 次多项式 $f\left(x\right)$ 使得这 $n+1$ 个点都在 $f\left(x\right)$ 上.

这两种操作的实质就是将多项式在**系数表示****点值表示**间转化.
这两种操作的实质就是将多项式在**系数表示****点值表示**间转化

## References

[**Picks's Blog**](https://picks.logdown.com)

[**Miskcoo's Space**](https://blog.miskcoo.com)
Loading