工科数学分析大作业(三) 傅里叶级数
来源:互联网 发布:快播软件 编辑:程序博客网 时间:2024/06/05 04:34
研究课题:探索类问题7
给定多项式:
利用快速傅里叶变换实现多项式相乘,并分析算法的复杂度。
算法介绍:快速数论变换
模 P 意义下的多项式
多项式乘法
如果多项式
多项式的表示方法
系数表示法:
对一个次数界为
系数表示法下的多项式乘法
假设
点值表示法:
一个次数界为
如果任意选取
求值计算的逆(从一个多项式的点值表示确定其系数表示中的系数)称为插值。
多项式插值的唯一性
对于任意
证明: 证明过程的基础是某个矩阵存在逆矩阵,
左边的矩阵是范德蒙德矩阵,表示为
点值表示法下的多项式乘法
点值表示法对于多项式乘法是很方便的。如果
如果已知
如果已知两个扩充点值形式的输入多项式,使其相乘而得到点值形式的结果需要
原根
设
假设
系数形式表示的多项式的快速乘法
能否采用利用关于点值形式的表示的多项式的线性时间乘法算法,来加快系数形式表示的多项式乘法运算的速度呢?答案依赖于能否快速把一个多项式在系数形式和点值形式之间转换。
当模数特殊的时候,当
本文中之后所提到的所有模数
NTT和FNTT
模P意义下的单位根
类似于复数域上单位复根的定义,在模
值
同样,和复数域上的单位复根类似,
类比复数域上的单位复根,给出
相消引理:对任何整数
相消引理推论:
折半引理:如果
由相消引理的推论还能得出折半引理推论:
求和引理:对任意整数
求和引理推论:对任意整数
NTT
回顾一下,我们希望计算次数界为
向量
FNTT
通过一种称为快速数论变换(FNTT),就可以在
FNTT方法运用了分治策略,它用
注意,
这样,求
递归基为
时间复杂度为
因此,运用快速数论变换,可以在
对单位根进行插值
现在来说明如何在单位根处进行插值,以便把一个多项式从点值表示转化为系数表示。按如下方式进行插值:把NTT写成一个矩阵方程,然后再检查其逆矩阵的形式。
根据
对
对于逆运算
定理:对
证明: 对
在求出逆矩阵
其中
对比 NTT, INTT 只要把对应位置用到的单位根
因此INTT的时间复杂度和NTT一样也是
卷积定理:对任意两个长度为
其中
因此,利用这种方法,可以在
有效的FNTT实现
使用迭代的方式避免递归,预处理出原向量中每个位置在最底层被交换到的位置,预处理出
假设进行 FNTT 的多项式的次数界为
通过递推求
探索类问题7(1)
给出两个多项式
令
令
时间复杂度为
用来解决问题
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define max_N (1<<10)#define P 998244353#define g 3//g is the Primitive Root of Pint n,inv_n;//n is a power of 2 which represents the.degree-bound of the polynomial//inv_n is the multiplicative inverse of n mod Pint brc[max_N];//binary reversing copy int w[2][max_N];//twiddle factors,w[0] for DFT while w[1] for IDFTint qpow(int x,int k){//an effective algorithm to calculate x^k mod P int ans=1; for(;k;x=1ll*x*x%P,k>>=1) if(k&1)ans=1ll*ans*x%P; return ans;}void fft_init(int l){//n is equal to 1<<l //calculating the multiplicative inverse of n mod P inv_n=qpow(n,P-2);//due to Fermat Theory //calculate twiddle factors mod P int G=qpow(g,(P-1)/n); w[0][0]=w[1][0]=1; for(int i=1;i<n;++i)w[0][i]=1ll*w[0][i-1]*G%P; for(int i=1;i<n;++i)w[1][i]=w[0][n-i]; //calculate binary reversions for(int i=1;i<n;++i)brc[i]=(brc[i>>1]>>1)^((i&1)<<(l-1));}void fft(int*a,int t){//Fast Fourier Transform//if t==0, do the Discrete Fourier Transform//if t==1, do the Inverse Discrete Fourier Transform //binary reversing copy for(int i=1;i<n;++i)if(i<brc[i])swap(a[i],a[brc[i]]); //the butterfly operation for(int i=2;i<=n;i<<=1) for(int j=0;j<n;j+=i) for(int k=0;k<(i>>1);++k){ int tmp=1ll*a[(i>>1)+j+k]*w[t][n/i*k]%P; a[(i>>1)+j+k]=(a[j+k]-tmp+P)%P; a[j+k]=(a[j+k]+tmp)%P; } //do not forget this step when doing IDFT if(t)for(int i=0;i<n;++i)a[i]=1ll*a[i]*inv_n%P;}int a[max_N],b[max_N],c[max_N];int main(){ freopen("answer1.txt","w",stdout);//output to a file named "answer1.txt" //initialize the coeffient of two polynomials for(int i=0;i<(1<<3);++i)a[i]=(i+1)*(i+1); for(int i=0;i<(1<<4);++i)b[i]=i+1; n=1<<5,fft_init(5);//initialize fft fft(a,0),fft(b,0); //compute the point-value representation of a and b for(int i=0;i<n;++i)c[i]=1ll*a[i]*b[i]%P; //a*b mod P can be linearly calculated in point-value representation fft(c,1); //compute the coeffient of a*b //ascending print the coeffient //the answer is x^2*a*b puts("0\n0"); for(int i=0;i<n;++i)printf("%d\n",c[i]); puts(""); return 0;}
该代码将积多项式的系数逐行升序输出到当前目录下的文件”Answer1.txt”中,文件共有
另附验证”Answer1.txt”的正确性的代码如下:
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;int gint(){ char c; int f=1; while(c=getchar(),c<48||c>57) if(c=='-')f=-1; int x=0; for(;c>47&&c<58;c=getchar()){ x=x*10+c-48; } return x*f;}#define max_N 2333int n,a[max_N],b[max_N],c[max_N];int main(){ freopen("answer1.txt","r",stdin); for(int i=0;i<(1<<3);++i)a[i]=(i+1)*(i+1); for(int i=0;i<(1<<4);++i)b[i]=i+1; n=1<<5; for(int i=0;i<n;++i) for(int j=0;j<=i;++j){ c[i]+=a[j]*b[i-j]; } gint(),gint(); for(int i=0,x;i<n;++i) if(gint()!=c[i]){ puts("WrongAnswer!"); for(;;); } puts("Accepted!"); for(;;); return 0;}
该验证代码用
探索类问题7(2)
给定多项式
同
令
中国剩余定理
若正整数
有整数解,并且在模
因此,我们可以分别计算出积多项式在若干个不同的素数模意义下的系数向量,只要保证这些模数的乘积大于
取
分别计算出积多项式在模
一共进行了
用来解决问题
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define max_N (1<<21)const int P[]={469762049,998244353,1004535809};//these prime numbers have the same primitive root#define g 3//the primitive root of Pint n,l,inv_n;//n is a power of 2 which represents the.degree-bound of the polynomial//n is equal to 1<<l, inv_n is the multiplicative inverse of n mod Pint brc[max_N];//binary reversing copyint w[2][max_N];//twiddle factors, w[0] for DFT, w[1] for IDFTint qpow(int x,int k,int P){//an effective algorithm to computing x^k mod P int ans=1; for(;k;x=1ll*x*x%P,k>>=1) if(k&1)ans=1ll*ans*x%P; return ans;}void fft_init(int k){ //calculating the multiplicative inverse of n mod P[k] inv_n=qpow(n,P[k]-2,P[k]);//due to Fermat Theory //computing twiddle factors mod P[k] int G=qpow(g,(P[k]-1)/n,P[k]); w[0][0]=w[1][0]=1; for(int i=1;i<n;++i)w[0][i]=1ll*w[0][i-1]*G%P[k]; for(int i=1;i<n;++i)w[1][i]=w[0][n-i];}void fft(int*a,int d,int t){//Fast Fourier Transform mod P[d] //if t==0, do the Discrete Fourier Transform //if t==1, do the Inverse Discrete Fourier Transform for(int i=1;i<n;++i)if(i<brc[i])swap(a[i],a[brc[i]]); //butterfuly operation for(int i=2;i<=n;i<<=1) for(int j=0;j<n;j+=i) for(int k=0;k<(i>>1);++k){ int tmp=1ll*a[(i>>1)+j+k]*w[t][n/i*k]%P[d]; a[(i>>1)+j+k]=(a[j+k]-tmp+P[d])%P[d]; a[j+k]=(a[j+k]+tmp)%P[d]; } //do not forget this step when doing IDFT; if(t)for(int i=0;i<n;++i)a[i]=1ll*a[i]*inv_n%P[d];}int a[max_N],b[max_N],c[max_N];__int128 ans[max_N];inline void print(__int128 x){//system can not print __int128 directly ... if(!x){ putchar('0'); return; } static char c[40]; int l=0; while(x)c[++l]=48+x%10,x/=10; while(l)putchar(c[l--]); putchar('\n');}int main(){ freopen("answer2.txt","w",stdout);//output to the file named "answer2.txt" n=1<<21,l=21; //computing binary reversions for(int i=1;i<n;++i)brc[i]=(brc[i>>1]>>1)^((i&1)<<(l-1)); __int128 p=__int128(P[0])*P[1]*P[2]; for(int k=0;k<3;++k){ //initialize the coeffient mod P[k] of two polynomials for(int i=0;i<(1<<20);++i)a[i]=1ll*(i+1)*(i+1)%P[k]; for(int i=(1<<20);i<(1<<21);++i)a[i]=0; for(int i=0;i<(1<<20);++i)b[i]=i+1; for(int i=(1<<20);i<(1<<21);++i)b[i]=0; fft_init(k);//initialize fft fft(a,k,0),fft(b,k,0); //compute the point-value representation of a and b for(int i=0;i<n;++i)c[i]=1ll*a[i]*b[i]%P[k]; //a*b mod P can be linearly calculated in point-value representation fft(c,k,1); //compute the coeffient of a*b mod P[k] //compute the coeffient of a*b with Chinese remainder theorem __int128 N=1ll*P[(k+1)%3]*P[(k+2)%3]; __int128 M=qpow(N%P[k],P[k]-2,P[k]); for(int i=0;i<n;++i)ans[i]=ans[i]+N*M*c[i]%p; } //ascending print the coeffient //the answer is x^2*a*b puts("0\n0"); for(int i=0;i<n;++i)print(ans[i]%p); puts(""); return 0;}
该代码将积多项式的系数逐行升序输出到当前目录下的文件”Answer2.txt”中,文件共有
另附验证”Answer2.txt”的正确性的代码如下:
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define max_N (1<<21)+5const int P[]={469762049,998244353,1004535809};int qpow(int x,int k,int P){//an effective algorithm to computing x^k mod P int ans=1; for(;k;x=1ll*x*x%P,k>>=1) if(k&1)ans=1ll*ans*x%P; return ans;}int n,c[3][max_N];int sum2(int n,int inv,int P){//sum i^2 mod P, inv=1/6 mod P return 1ll*n*(n+1)%P*(n<<1|1)%P*inv%P;}int sum3(int n,int P){//sum i^3 mod P int tmp=1ll*n*(n+1)/2%P; return 1ll*tmp*tmp%P;}int main(){ freopen("answer2.txt","r",stdin); n=1<<21; for(int i=0;i<n+2;++i){ char ch; while(ch=getchar(),ch<48||ch>57); for(;ch>47&&ch<58;ch=getchar()) for(int k=0;k<3;++k){ c[k][i]=(1ll*c[k][i]*10+ch-48)%P[k]; } } for(int k=0;k<3;++k){ int inv=qpow(12,P[k]-2,P[k]);// 1/12 int inv1=qpow(6,P[k]-2,P[k]);// 1/6 int tmp1=sum2(1<<20,inv1,P[k]); int tmp2=sum3(1<<20,P[k]); for(int i=2,ans;i<n+2;++i){ if(i<=(1<<20)){ ans=1ll*i*i%P[k]; ans=1ll*ans*(ans-1)%P[k]; ans=1ll*ans*inv%P[k]; ans=(ans+P[k])%P[k]; } else{ ans=1ll*i*(tmp1-sum2(i-(1<<20)-1,inv1,P[k]))%P[k]; ans=(ans-tmp2+sum3(i-(1<<20)-1,P[k]))%P[k]; ans=(ans+P[k])%P[k]; } if(ans!=c[k][i]){ printf("%d %d\n",k,i); printf("%d %d\n",ans,c[k][i]); puts("WrongAnswer!"); for(;;); } } } puts("Accepted!"); for(;;); return 0;}
通过推公式的方法,用
上面所提到的公式为:
其中
- 工科数学分析大作业(三) 傅里叶级数
- POJ3244(工科数学分析)
- HDU3113(工科数学分析之分解)
- 工科数学分析-微积分(1)
- 漫步数学分析九——级数
- 陈纪修老师《数学分析》 第09章:数项级数 笔记
- 陈纪修老师《数学分析》 第10章:函数项级数 笔记
- 【JAVA大作业开发记录(三)】
- 数学分析
- 数学分析
- 傅里叶级数
- 傅里叶级数
- 傅里叶级数
- 漫步数学分析三——开集
- 数学分析八讲笔记(三)
- 漫步数学分析二十三——级数的积分与微分
- 大数据系统与大规模数据分析 之 作业三
- 大作业
- js的事件冒泡和事件捕获
- Python初遇问题5.16
- Appium 切换Webview模式,页面还停留在原来页面的问题
- Bootstrap栅格系统的精妙之处
- Oil Deposits
- 工科数学分析大作业(三) 傅里叶级数
- SURF特征提取分析
- Linux Centos 6.6安装Mysql
- url打开app
- Android 开发—— 小工具,大效率
- acm 香港网络赛D题
- Spark日志清洗一般流程(Python版)
- 网页布局
- ViewPager无限轮播(网络图片