关于傅里叶变换NTT(FNT)的周边
2015-09-18 01:03
549 查看
NTT:快速数论变化,对于FFT精度减少的情况,NTT可以避免但是会慢一点,毕竟是数论有Mod,和快速米
引用:/article/2081366.html
周边介绍。
利用原根,在ZP整数域(后悔没学好《信息安全数学基础》
原根介绍:http://baike.baidu.com/link?url=2gVDOcvJL0eTySKDiwFaDE7hNOTSJ087eGtv42QCt8tYEJZyUMXb6Eb40n0E0ygRoj4unNtEwukv3AFD1IEeia
然后对于一个整数域中的值分别对应一个数,具体看下这类数学书,用来替代单位根
对于一个P(素数)
比较快的一种方法找原根:http://blog.csdn.net/zhang20072844/article/details/11541133 (ORZ
大概是对于P的一个大于1的因子满足G^因子%P==1,那么就不是原根,原根很小。
其他跟FFT没区别。
其实傅里叶变化关键还是能够化成卷积的形式(这里只是处理普通和答案要求Mod的时候)
要求答案的逆,和除法,要看Picks的博客:
NTT:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int N=1<<18;
const int P=998244353;
const int G=3;
const int NUM=20;
ll wn[NUM],a
,b
;
char A
,B
;
ll Pow(ll a,ll b,ll m)
{
ll ans=1;
a%=m;
while (b)
{
if (b&1) ans=ans*a%m;
a=a*a%m;
b/=2;
}
return ans;
}
void Getwn()
{
for (int i=0;i<NUM;i++)
{
int t=1<<i;
wn[i]=Pow(G,(P-1)/t,P);
}
}
void Rader(ll a[],int len)
{
int j=len>>1;
for (int i=1;i<len-1;i++)
{
if (i<j) swap(a[i],a[j]);
int k=len>>1;
while (j>=k)
{
j-=k;
k>>=1;
}
if (j<k) j+=k;
}
}
void NTT(ll a[],int len,int on)
{
Rader(a,len);
int id=0;
for (int h=2;h<=len;h<<=1)
{
id++;
for (int j=0;j<len;j+=h)
{
ll w=1;
for (int k=j;k<j+h/2;k++)
{
ll u=a[k]%P;
ll t=w*(a[k+h/2]%P)%P;
a[k]=(u+t)%P;
a[k+h/2]=((u-t)%P+P)%P;
w=w*wn[id]%P;
}
}
}
if (on==-1)
{
for (int i=1;i<len/2;i++)
swap(a[i],a[len-i]);
ll inv=Pow(len,P-2,P);
for (int i=0;i<len;i++)
a[i]=a[i]%P*inv%P;
}
}
void Conv(ll a[],ll b[],int n)
{
NTT(a,n,1);
NTT(b,n,1);
for (int i=0;i<n;i++)
a[i]=a[i]*b[i]%P;
NTT(a,n,-1);
}
int pan(char s[],char ss[])
{
int len=strlen(s);
len--;
while (s[len]=='0'&&len>=0) len--;
if (len<0) return 1;
len=strlen(ss);
len--;
while (ss[len]=='0'&&len>=0) len--;
if (len<0) return 1;
return 0;
}
int main()
{
Getwn();
while (scanf("%s%s",A,B)!=EOF)
{
if (pan(A,B))
{
puts("0");
continue;
}
int len=1;
int lenA=strlen(A);
int lenB=strlen(B);
while (len<=2*lenA||len<=2*lenB) len<<=1;
for (int i=0;i<lenA;i++)
A[len-1-i]=A[lenA-1-i];
for (int i=0;i<len-lenA;i++) A[i]='0';
for (int i=0;i<lenB;i++)
B[len-1-i]=B[lenB-1-i];
for (int i=0;i<len-lenB;i++) B[i]='0';
for (int i=0;i<len;i++) a[len-1-i]=A[i]-'0';
for (int i=0;i<len;i++) b[len-1-i]=B[i]-'0';
Conv(a,b,len);
int t=0;
for (int i=0;i<len;i++)
{
a[i]+=t;
if (a[i]>9)
{
t=a[i]/10;
a[i]%=10;
}
else t=0;
}
len--;
while (a[len]==0) len--;
for (int i=len;i>=0;i--) printf("%d",a[i]);
puts("");
}
return 0;
}
引用:/article/2081366.html
周边介绍。
利用原根,在ZP整数域(后悔没学好《信息安全数学基础》
原根介绍:http://baike.baidu.com/link?url=2gVDOcvJL0eTySKDiwFaDE7hNOTSJ087eGtv42QCt8tYEJZyUMXb6Eb40n0E0ygRoj4unNtEwukv3AFD1IEeia
然后对于一个整数域中的值分别对应一个数,具体看下这类数学书,用来替代单位根
对于一个P(素数)
比较快的一种方法找原根:http://blog.csdn.net/zhang20072844/article/details/11541133 (ORZ
大概是对于P的一个大于1的因子满足G^因子%P==1,那么就不是原根,原根很小。
其他跟FFT没区别。
其实傅里叶变化关键还是能够化成卷积的形式(这里只是处理普通和答案要求Mod的时候)
要求答案的逆,和除法,要看Picks的博客:
NTT:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int N=1<<18;
const int P=998244353;
const int G=3;
const int NUM=20;
ll wn[NUM],a
,b
;
char A
,B
;
ll Pow(ll a,ll b,ll m)
{
ll ans=1;
a%=m;
while (b)
{
if (b&1) ans=ans*a%m;
a=a*a%m;
b/=2;
}
return ans;
}
void Getwn()
{
for (int i=0;i<NUM;i++)
{
int t=1<<i;
wn[i]=Pow(G,(P-1)/t,P);
}
}
void Rader(ll a[],int len)
{
int j=len>>1;
for (int i=1;i<len-1;i++)
{
if (i<j) swap(a[i],a[j]);
int k=len>>1;
while (j>=k)
{
j-=k;
k>>=1;
}
if (j<k) j+=k;
}
}
void NTT(ll a[],int len,int on)
{
Rader(a,len);
int id=0;
for (int h=2;h<=len;h<<=1)
{
id++;
for (int j=0;j<len;j+=h)
{
ll w=1;
for (int k=j;k<j+h/2;k++)
{
ll u=a[k]%P;
ll t=w*(a[k+h/2]%P)%P;
a[k]=(u+t)%P;
a[k+h/2]=((u-t)%P+P)%P;
w=w*wn[id]%P;
}
}
}
if (on==-1)
{
for (int i=1;i<len/2;i++)
swap(a[i],a[len-i]);
ll inv=Pow(len,P-2,P);
for (int i=0;i<len;i++)
a[i]=a[i]%P*inv%P;
}
}
void Conv(ll a[],ll b[],int n)
{
NTT(a,n,1);
NTT(b,n,1);
for (int i=0;i<n;i++)
a[i]=a[i]*b[i]%P;
NTT(a,n,-1);
}
int pan(char s[],char ss[])
{
int len=strlen(s);
len--;
while (s[len]=='0'&&len>=0) len--;
if (len<0) return 1;
len=strlen(ss);
len--;
while (ss[len]=='0'&&len>=0) len--;
if (len<0) return 1;
return 0;
}
int main()
{
Getwn();
while (scanf("%s%s",A,B)!=EOF)
{
if (pan(A,B))
{
puts("0");
continue;
}
int len=1;
int lenA=strlen(A);
int lenB=strlen(B);
while (len<=2*lenA||len<=2*lenB) len<<=1;
for (int i=0;i<lenA;i++)
A[len-1-i]=A[lenA-1-i];
for (int i=0;i<len-lenA;i++) A[i]='0';
for (int i=0;i<lenB;i++)
B[len-1-i]=B[lenB-1-i];
for (int i=0;i<len-lenB;i++) B[i]='0';
for (int i=0;i<len;i++) a[len-1-i]=A[i]-'0';
for (int i=0;i<len;i++) b[len-1-i]=B[i]-'0';
Conv(a,b,len);
int t=0;
for (int i=0;i<len;i++)
{
a[i]+=t;
if (a[i]>9)
{
t=a[i]/10;
a[i]%=10;
}
else t=0;
}
len--;
while (a[len]==0) len--;
for (int i=len;i>=0;i--) printf("%d",a[i]);
puts("");
}
return 0;
}
相关文章推荐
- python MySQLdb使用
- JS函数构造: 类 、对象和函数的调用
- 客户端技术的一点思考
- 自己做的一种计算器算法的设计..不知道代码是否有bug
- 网络开始---多线程---NSThread-02-线程状态(了解)(三)
- HDU 2026 首字母变大写
- 网络开始---多线程---NSThread-01-基本使用(了解)(二)
- IntelliJ IDEA 14 注册码
- 2015校招前端面试题
- Quick-cocos2d-x3.3 Study (四)--------- 添加标题,并上下运动
- DO-214 SMA、SMB、SMC封装
- GUN,Linux,Linux发行版
- 网络开始---多线程---阻塞主线程(演示)(一)
- Andriod开发环境的发展演变
- HDU 查找最大元素 2025
- Java学习-033-JavaWeb_002 -- 网页标记语言JSP基础知识
- Quick-cocos2d-x3.3 Study (三)--------- 创建一个背景
- 线程间通信与同步
- 单例模式简介以及C++版本的实现
- 10 个 Python IDE 和代码编辑器