首页 > 其他分享 >任意模数多项式乘法(MTT)学习笔记

任意模数多项式乘法(MTT)学习笔记

时间:2023-06-13 20:22:23浏览次数:40  
标签:Int mo3 int 多项式 ll MTT 模数 mo2 mo1

三模数 NTT

常数大、速度慢、精度高是它的特点。

在考虑三模数 NTT 之前先考虑一下中国剩余定理吧。

已知

\[\begin{cases} x\equiv x_1(\bmod m_1)\\ x\equiv x_2(\bmod m_2)\\ x\equiv x_3(\bmod m_3)\\ \end{cases} \]

求 \(x\bmod m_1m_2m_3\)。

\[\begin{aligned} &k_1m_1+x_1=k_2m_2+x_2\\ &k_1\equiv \frac{x_2-x_1}{m_1}(\bmod m_2)\\ &x\equiv k_1m_1+x_1(\bmod m_1m_2)\\ &x_4=(k_1m_1+x_1)\bmod m_1m_2\\ &k_4m_1m_2+x_4=k_3m_3+x_3\\ &k_4\equiv \frac{x_3-x_4}{m_1m_2}(\bmod m_1m_2m_3)\\ &x\equiv k_4m_4+x_4(\bmod m_1m_2m_3)\\ \end{aligned} \]

一点疑惑的解答(自言自语):

因为 \(k_1\equiv \frac{x_2-x_1}{m_1}(\bmod m_2)\),所以 \(k_1=\frac{x_2-x_1}{m_1}+km_2\)。又因为 \(k_1m_1\le m_1m_2\),所以 \(k_1\le m_2\)。所以 \(k\ge 0\),所以 \(k_1\) 最小为 \(\frac{x_2-x_1}{m_1}\),即 \(x\equiv k_1m_1+x_1(\bmod m_1m_2)\\\)。

进入正题:

所谓的三模数 NTT 指的是 以 \(998244353,1004535809,469762049\) 为模数(经典 NTT 模数,原根均为 \(3\))分别进行 NTT,最后用上文的计算方式计算即可。

因为以上三个模数的乘积为很大,一般答案即使不取模也不会大于该数,所以上式的 \(k_4m_4+x_4\) 就是原答案,直接对题目给出的模数取模即可。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define Big __int128
const int N=3e5+1;
const ll mo1=998244353,mo2=1004535809,mo3=469762049,G=3;
inline Big Ksm(Big x,Big y,ll mo){
    Big res=1;
    for(;y;y>>=1,x=x*x%mo)
        if(y&1)res=res*x%mo;
    return res;
}
ll MOD;
const ll inv1=Ksm(mo1,mo2-2,mo2),inv2=Ksm(mo1*mo2%mo3,mo3-2,mo3);
struct Int{
    ll a,b,c;
    Int(ll _x=0){a=b=c=_x;}
    Int(ll _a,ll _b,ll _c){a=_a,b=_b,c=_c;}
    inline Int operator + (const Int &x){return Int((ll)(a+x.a)%mo1,(ll)(b+x.b)%mo2,(ll)(c+x.c)%mo3);}
    inline Int operator - (const Int &x){return Int((ll)(a-x.a+mo1)%mo1,(ll)(b-x.b+mo2)%mo2,(ll)(c-x.c+mo3)%mo3);}
    inline Int operator * (const Int &x){return Int((ll)a*x.a%mo1,(ll)b*x.b%mo2,(ll)c*x.c%mo3);}
    inline Int operator * (ll x){return Int((ll)a*x%mo1,(ll)b*x%mo2,(ll)c*x%mo3);}
    void mulinv(ll x){
        a=a*Ksm(x,mo1-2,mo1)%mo1;
        b=b*Ksm(x,mo2-2,mo2)%mo2;
        c=c*Ksm(x,mo3-2,mo3)%mo3;
    }
    void inv(){
        a=Ksm(a,mo1-2,mo1)%mo1;
        b=Ksm(b,mo2-2,mo2)%mo2;
        c=Ksm(c,mo3-2,mo3)%mo3;
    }
    ll gettrue(){
        Big x=(Big)(b-a+mo2)%mo2*inv1%mo2*(Big)mo1+(Big)a;
        return (((Big)(c-x%mo3+mo3)%mo3*inv2%mo3*(mo1%MOD*mo2%MOD)%MOD+x%MOD)%MOD+MOD)%MOD;
    }
}; // mtt
int rev[N];
Int w[N];
void NTT(Int *a,int Len,bool type){
    for(int i=0;i<Len;i++){
        rev[i]=(rev[i>>1]>>1)+(i&1?Len>>1:0);
        if(rev[i]>i)swap(a[rev[i]],a[i]);
    }
    for(int d=1;d<Len;d<<=1){
        Int W=Int(Ksm(G,(mo1-1)/(d*2),mo1),Ksm(G,(mo2-1)/(d*2),mo2),Ksm(G,(mo3-1)/(d*2),mo3));
        if(type)W.inv();
        w[0]=Int(1); for(int i=1;i<d;i++)w[i]=w[i-1]*W;
        for(int fir=0;fir<Len;fir+=d<<1){
            int sec=fir+d;
            for(int i=0;i<d;i++){
                Int a0=a[fir+i],a1=w[i]*a[sec+i];
                a[fir+i]=a0+a1,a[sec+i]=a0-a1;
            }
        }
    }
    if(type){for(int i=0;i<Len;i++)a[i].mulinv(Len);}
}
int n,m;
Int f[N],g[N];
int main(){
    cin>>n>>m>>MOD;
    for(int i=0,x;i<=n;i++)cin>>x,x%=MOD,f[i]=Int(x);
    for(int i=0,x;i<=m;i++)cin>>x,x%=MOD,g[i]=Int(x);
    int Len=1;
    while(Len<=(n+m+4))Len<<=1;
    NTT(f,Len,0),NTT(g,Len,0);
    for(int i=0;i<Len;i++)f[i]=f[i]*g[i];
    NTT(f,Len,1);
    for(int i=0;i<=n+m;i++)cout<<f[i].gettrue()<<' ';
    cout<<'\n';
    return 0;
}

拆系数 FFT

常数小,速度快,精度低(\(\operatorname{long double}\) 信仰跑)是它的特色。

如果直接对原数列进行 FFT 的话会炸精度的。考虑拆系数,即 \(A_i=J\times A'_i+A''_i\)(\(A''_i< J\))。

那么:

\[\begin{aligned} F&=A\times B=(J\times A'+A'')\times(J\times B'+B'')\\ &=J^2A'B'+J(A'B''+A''B')+A''B''\\ \end{aligned} \]

如果直接计算的话需要四次 dft,三次 idft,和九次 ntt 的三模数 NTT 差距并不大。

考虑优化,然而 dft/idft 中有什么地方没有用到捏?虚部!考虑将 \(A'\),\(A''\),\(B'\),\(B''\) 合并在一起进行 dft。

设:

\[\begin{aligned} P_i=A'_i+A''_ii\\ Q_i=A'_i-A''_ii\\ E_i=B'_i+B''_ii\\ \end{aligned} \]

有:

\[\begin{aligned} &W_i=(P\times E)_i=(A'_iB'_i-A''_iB''_i)+(A'_iB''_i+A''_iB'_i)i\\ &R_i=(Q\times E)_i=(A'_iB'_i+A''_iB''_i)+(A'_iB''_i-A''_iB'_i)i\\ \end{aligned} \]

我们可以通过 \(W\) 和 \(R\) 的加减得到我们想要的系数。

\[\begin{aligned} &W_i+R_i=2\times(A'_iB'_i+A'B''_ii)\\ &R_i-W_i=2\times(A''_iB''_i+A''_iB'_ii)\\ \end{aligned} \]

注意: 是先除以二再取整!!!(代码 \(\texttt{39}\) 行)。

#include <bits/stdc++.h>
#define poly vector<int>
using namespace std;
const int N=5e5+11;
int mo;
const int base=32768;
namespace Poly{
    using db = long double;
    const db pi=acos(-1);
    struct cp{
        db x,y;
        cp operator + (const cp &a){return {x+a.x,y+a.y};}
        cp operator - (const cp &a){return {x-a.x,y-a.y};}
        cp operator * (const cp &a){return {x*a.x-y*a.y,x*a.y+y*a.x};}
    };
    cp w[N]; int rev[N];
    void init_rev(int Len){
        for(int i=0;i<Len;i++)
            rev[i]=(rev[i>>1]>>1)+(i&1?Len>>1:0);
    }
    void FFT(cp *a,int Len,bool type){
        for(int i=0;i<Len;i++)if(rev[i]>i)swap(a[rev[i]],a[i]);
        for(int d=1;d<Len;d<<=1){
            cp W={cos(pi/d),sin(pi/d)};
            if(type)W.y=-W.y;
            w[0]={1,0};
            for(int i=1;i<d;i++)w[i]=w[i-1]*W;
            for(int fir=0;fir<Len;fir+=d<<1){
                int sec=fir+d;
                for(int i=0;i<d;i++){
                    cp a0=a[fir+i],a1=w[i]*a[sec+i];
                    a[fir+i]=a0+a1,a[sec+i]=a0-a1;
                }
            }
        }
        if(type)for(int i=0;i<Len;i++)a[i].x/=Len,a[i].y/=Len;
    }
    cp f[N],g[N],e[N];
    long long C(db x){return (long long)(x/2.+0.5)%mo;} // important!!!
    poly mul(poly x,poly y){
        int tot=x.size()+y.size()-1,Len=1;
        while(Len<=(tot+2))Len<<=1;
        init_rev(Len);
        for(int i=0;i<=Len;i++)f[i]=g[i]=e[i]={0,0};
        for(int i=0;i<x.size();i++){
            int a0=x[i]/base,a1=x[i]%base;
            f[i]={a0,a1},g[i]={a0,-a1};
        }
        for(int i=0;i<y.size();i++){
            int b0=y[i]/base,b1=y[i]%base;
            e[i]={b0,b1};
        }
        FFT(f,Len,0),FFT(g,Len,0),FFT(e,Len,0);
        for(int i=0;i<Len;i++)f[i]=f[i]*e[i],g[i]=g[i]*e[i];
        FFT(f,Len,1),FFT(g,Len,1);
        poly ret(tot,0);
        for(int i=0;i<tot;i++){
            ret[i]=1ll*base*base%mo*(C(f[i].x+g[i].x))%mo;
            ret[i]+=1ll*base*((C(f[i].y+g[i].y))+(C(f[i].y-g[i].y)))%mo;
            ret[i]%=mo;
            ret[i]+=(C(g[i].x-f[i].x))%mo;
            ret[i]%=mo;
        }
        return ret;
    }
} using Poly::mul;
int a[N],n,m;
poly solve(int l,int r){
    if(l==r)return {1,a[l]};
    int mid=l+r>>1;
    return mul(solve(l,mid),solve(mid+1,r));
}
int main(){
    cin>>n>>m>>mo;
    poly a(n+1,0),b(m+1,0);
    for(int i=0;i<=n;i++) cin>>a[i];
    for(int i=0;i<=m;i++) cin>>b[i];
    a=mul(a,b);
    for(int i:a)cout<<i<<' ';
    return 0;
}

标签:Int,mo3,int,多项式,ll,MTT,模数,mo2,mo1
From: https://www.cnblogs.com/dadidididi/p/17478662.html

相关文章

  • Python一句话实现秦九韶算法快速计算多项式的值
    关于秦九韶算法快速计算多项式值的原理描述请参考之前推送的文章Python使用秦九韶算法求解多项式的值。本文重点演示Python函数reduce()和lambda表达式的用法。代码没加注释,如果不好理解的话,可以先参考文末相关阅读中的介绍。......
  • Python使用scipy进行多项式计算与符号计算
    本文代码主要演示如何使用poly1d进行多项式计算和符号计算。fromscipyimport>>>p1=poly1d([1,2,3,4])#输出结果中,第一行的数字为第二行对应位置项中x的指数>>>print(p1)321x+2x+3x+4#等价于p2=(x-1)(x-2)(x-3)(x-4)>>>p2=poly1d([1,2,3......
  • R数据分析:多项式回归与响应面分析的理解与实操
    今天给大家分享一个新的统计方法,叫做响应面分析,响应面分析是用来探究变量一致性假设的(Congruencehypotheses)。本身是一个工程学方法,目前在组织行为学,管理,市场营销等等领域中使用越来越多。Congruencehypothesesstatethattheagreement(i.e.,congruence)betweentwoconst......
  • 整数域上的多项式辗转相除
    题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1892 题意:求两个多项式的最大的公共多项式。 #include<iostream>#include<string.h>#include<algorithm>#include<stdio.h>#include<vec......
  • 多项式回归模型(Office Prices)
     分析:还是上次的房价预测题目,指明要用多项式回归拟合。在多元多项式拟合时候,目标函数表示如下         对其目标函数求偏导得到         很容易写出代码。代码:#coding:utf-8importmathclassData: def__init__(self): self.x=[] self.y=0.0d......
  • 基于FPGA的LFSR16位伪随机数产生算法实现,可以配置不同的随机数种子和改生成多项式,包
    1.算法仿真效果vivado2019.2仿真结果如下:2.算法涉及理论知识概要LFSR(线性反馈移位寄存器)提供了一种在微控制器上快速生成非序列数字列表的简单方法。生成伪随机数只需要右移操作和XOR操作。LFSR完全由其多项式指定。例如,6千-次多项式与每个项存在用方程x表示6+x5+x4+x3......
  • 如何科学地利用MTTR优化软件交付流程?
    谷歌提出的衡量DevOps质量的DORA指标让MTTR(平均恢复时间)名声大振。在本文中,你将了解到MTTR的作用、为什么它对行业研究很有用、你可能被它误导的原因以及如何避免MTTR产生的弊端。 MTTR究竟是在测量什么?MTTR指平均恢复时间,既是MeanTimetoRecovery,有时也是Mea......
  • 小灰灰机器学习day3——多项式拟合(最高项系数为2)
    importnumpyasnpTime=np.array([1,2,4,8,16,32,64])Temp=np.array([0,1,2,3,4,5,6])importmatplotlib.pyplotaspltplt.figure()plt.plot(Time,Temp,'bo')plt.xlabel("Time")plt.ylabel("Temp")plt.title(�......
  • (ADI)AD7276ARMZ高速模数转换器、A4980KLPTR汽车步进电机驱动器
    AD7276ARMZ是(ADI)的高速模数转换器、数模转换器和AFE/CODEC构成了业界知名的高速数据转换器产品组合。该器件具有出色性能、高输入带宽和集成的信号处理,使设计人员能够轻松、可靠地选择合适的方案用于各种应用,包括仪表、军用/航天、无线/有线通信和医学影像。位数:12采样率(每......
  • 一元多项式的乘法与加法运算
    设计函数分别求两个一元多项式的乘积与和。输入格式:输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。输出格式:00。输入样例:434-5261-203520-7431输出样例:1......