• 数论算法 Plus


    好像有不少更新:)
    本文主要记录一些不是那么熟悉的高级数论算法的推导与应用。

    exBSGS算法

    解决模数、底数不互质的离散对数问题。

    (1)为何(BSGS)算法不再适用:(A)不一定存在逆元,而且无法保证解的循环性。
    (2)无解的结论:
    设方程为(A^x=B pmod{P})
    ((A,P) mid B)(B e 1) 时,原方程无自然数解。
    还有就是(A=0,B≠0)这种。
    (3)算法流程:
    先判无解。
    然后若(B=1),显然(x=0),特判之。
    否则 (G=(A,P))
    (G>1)
    两边同除以(G),得(A^{x-1}=frac{B}{G}*(frac{A}{G})^{-1} pmod{frac{P}{G}})
    迭代至((A,P)==1)时即可套用(BSGS)算法了。

    int BSGS(int a,int b,int P)
    {
        int s,o;
        S.clear();
        int m=(int)ceil(sqrt(P));
        o=a;
        s=b;
        for(int i=1; i<=m; i++)
        {
            s=(ll)s*o%P;
            S[s]=i;
        }
        o=fpow(a,m,P);
        s=1;
        for(int i=1; i<=m; i++)
        {
            s=(ll)s*o%P;
            if((it=S.find(s))!=S.end())
                return i*m-it->second;
        }
        return -1;
    }
    
    int exbsgs(int a,int b,int c)
    {
        a%=c; b%=c;
        if(b==1||c==1)return 0;
        if(!a)return b?-1:1;
        int d=gcd(a,c);
        if(d==1)return BSGS(a,b,c);
        if(b%d!=0)return -1;
        int o=exbsgs(a,(ll)b/d*Inv(a/d,c/d)%(c/d),c/d);
        return o==-1?-1:o+1;
    }
    

    快速计算阶乘

    即快速计算:
    对于质数(p),把(N!)去掉所有(p)因子的部分对(p^e)取模,(p^ele 10^5)
    (1)为了便于统计出现了多少个p的次幂,先将阶乘中所有p的倍数提出来。可以简单算出:共有
    $displaystyle lfloor frac{n}{p} floor $ 个。
    这中间每一项都除去p,
    可以得到 (displaystyle lfloor frac{n}{p} floor !) 。该部分可以选择递归求解。
    (2)那么接下来只剩下非p的倍数的几项了。通过简单观察可以知道,剩余几项具有循环节,循环节长度小于(p^e)
    原因是剩余项关于p具有循环节,而
    (x+p^e equiv x pmod{p^e})
    所以可以一起计算。
    结果会剩下几项凑不齐一个循环节,但是这几项长度已经小于(p^e)了,可以选择暴力乘法求解。
    有必要举个例子模拟一下:

    (N=22,p=3,e=2,)
    (22!=1*2*3*......*22)
    (=(1*2*4*5*7*8*10*11*13*14*16*17*19*20*22)*3^7*7!)
    (7!)递归处理;

    ((1*2*4*5*7*8*10*11*13*14*16*17*19*20*22))
    (equiv (1*2*4*5*7*8)^2*(19*20*22) pmod {3^2})
    两个括号里的暴力计算;
    考虑对 (p^e) 以内做预处理,查询复杂度可以做到 (O(log_p n)) 左右。
    代码在跟下面一起贴

    组合数取模(扩展卢卡斯)

    即快速计算(dbinom{N}{M} pmod{P})(N,Mle 10^9,P不一定是质数,Ple 10^9),但(P)中任何一个质因子的总积不超过(10^5)

    考虑中国剩余定理:
    (Ans=dbinom{N}{M})

    对质因子分开计算有:
    (Ans equiv a_1 pmod{p_1^{q_1}})
    (Ans equiv a_2 pmod{p_2^{q_2}})
    (......)
    对于一个式子:
    不含(p_i)的部分使用上述快速阶乘方法求解。
    含有(p_i)的重数使用(displaystyle sum_{i=1} lfloor frac{n}{p^i} floor)计算。
    然后两部分分开算,乘起来就是对应的(a_i)

    最后CRT合并起来就可以了。

    [Ansequiv sum a_i imes (prod_{j eq i}b_j^{-1} mod {b_i}) imes prod_{j eq i}b_j pmod {prod b_i} ]

    struct bnm {
    	int p, Mod, fac[1 << 20];
    	void init(int x, int y)
    	{
    		p = x;
    		Mod = y;
    		fac[0] = 1;
    		for(int s = 1; s != Mod; ++s)
    			if(s % p)
    				fac[s] = 1LL * fac[s - 1] * s % Mod;
    			else
    				fac[s] = fac[s - 1];
    	}
    	int Fac(ll x)
    	{
    		if(!x)
    			return 1;
    		return 1LL * Fac(x / p) * fexp(fac[Mod - 1], x / Mod, Mod) % Mod * fac[x % Mod] % Mod;
    	}
    	int operator()(ll n, ll m) {
    		if(n < m || m < 0) return 0;
    		int res = Fac(n) * Inv(Fac(m), Mod) % Mod * Inv(Fac(n - m), Mod) % Mod;
    		int z = 0;
    		for(ll d = n; d; z += d /= p);
    		for(ll d = m; d; z -= d /= p);
    		for(ll d = n - m; d; z -= d /= p);
    		res = 1LL * res * fexp(p, z, Mod) % Mod;
    		return res;
    	}
    }Solve;
    

    普通二次剩余

    即解方程(x^2=a pmod{P}),P是质数。

    (1)勒让德符号的定义
    ((frac{a}{p})=1):a在模p意义下有二次剩余。
    ((frac{a}{p})=-1):a在模p意义下无二次剩余。
    计算公式:((frac{a}{p}) = a^{frac{p-1}{2}} pmod p)
    (2)求解二次剩余
    随机一个(a)使得(b=sqrt{a^2-n})(此时 (a^2-n) 无二次剩余,就直接写成根号形式),然后把(b)作为二次域的单位元。
    那么(x=(a+b)^{frac{p+1}{2}})(证明需要二项式定理展开,具体参考这里 !ACdreamer
    写一个扩域乘法类就好了。
    注意这里(p)得是奇质数,但(p=2)也是很容易特判的。
    二次剩余有一正一负两解,下面代码随便取了一个。

    namespace Cipolla
    {
    	inline int Le(int x){return fpow(x,(P-1)/2);}
    	
    	int w;
    	struct Cp
    	{
    		int x,y;
    		Cp(int a=0,int b=0){x=a;y=b;}
    	};
    	Cp operator+(Cp a,Cp b){return Cp((a.x+b.x)%P,(a.y+b.y)%P);}
    	Cp operator-(Cp a,Cp b){return Cp((a.x-b.x+P)%P,(a.y-b.y+P)%P);}
    	Cp operator*(Cp a,Cp b){return Cp(((ll)a.x*b.x+(ll)w*a.y%P*b.y)%P,((ll)a.x*b.y+(ll)a.y*b.x)%P);}
    	Cp operator^(Cp a,int b){
    		Cp o(1,0);
    		for(;b;b>>=1) {
    			if(b&1)o=o*a;
    			a=a*a;
    		}
    		return o;
    	}
    	
    	int calc(int x)
    	{
    		x%=P;
    		int a;
    		while(1) {
    			a=rand();
    			w=((ll)a*a-x+P)%P;
    			if(Le(w)==P-1) break;
    		}
    		Cp s=Cp(a,1)^((P+1)/2);
    		return min(s.x,P-s.x);
    	}
    }
    

    原根和阶

    奇素数与奇素数的方幂有原根,原根 (g) 是阶恰好等于 (p-1) 的数,也就是 (g^x(xin [0,p-2])) 与整数 (yin [1,p-1]) 一一对应。
    我们一般用暴力枚举的方法求原根,原理类似试除法,判断是否存在 (d|p-1) 使得 (g^dequiv 1 pmod p) 即可。

    bool judge(int x)
    {
    	for(int j = 0; j < tot; j ++) //只需对质因子做
    		if(fexp(x, (P - 1) / c[j], P) == 1)
    			return false;
    	return true;
    }
    

    对一个任意模数求阶也不是很难,只要对 (varphi (m)) 不断试除保证 (x^kequiv 1 pmod m)即可。

    原根可以转换为单位根,即若 (n|(varphi(p)-1))(omega_n=g^{frac{varphi(p)-1}{n}})
    常用于单位根反演 ([n|i]=dfrac{1}{n}sum_{k=0}^{n-1} omega_n^{ki})
    单位根反演可以应用于 ( ext{NTT}) 以及结合“二项式定理”优化复杂度。

    快速乘法取模

    某些情况下机器不支持int128,或者毒瘤出题人卡int128常数,就需要写这种 (O(1)) 快速乘。

    inline ll mul(ll a,ll b,ll p){
    	ll res=a*b-((ll)((long double)a*b/p+0.5))*p;
    	return res<0?res+p:res;
    }
    

    一定要保证 (a,blt p) 的时候计算,因为它是由这个保证精度的。

    合并同余方程组

    (x equiv now pmod m)
    (x equiv a pmod b)

    由第一式得
    (x=now+k*m)
    代入二式
    (now+km equiv a pmod b)
    (mk equiv a-now pmod b)
    拿个扩欧就能解出来最小的(k)
    然后(now=now+k imes m)

    有一个坑点就是你的(k)不要对(m)取模,
    归纳易证你这个(now)一定是最小的正整数解,只要题目保证了啥啥,(now)就肯定不会溢出。

    但是好多题目中(m)都会溢出,变成负数,再取模就会出bug。

    当然还是不能排除求出假答案的情况,最后可以 check 一遍。

    void excrt(ll a,ll b)
    {
        ll c=((a-now)%b+b)%b;
        ll x,y;
        ll d=exgcd(M,b,x,y);
        if(c%d!=0) err();
        c/=d;
        b/=d;
        x=(x%b+b)%b;
        x=(__int128)x*c%b;
        now+=x*M;
        M*=b;
    }
    

    Miller-Rabin素数测试

    二次探测原理:
    当p是质数时,则一定有

    (x^2 equiv 1 pmod p),则(x=1 pmod p)(x=-1 pmod p)

    这样我们就考虑反过来想办法验证(n)是质数。

    (n=2^k imes m + 1)(m是奇数),

    拿个小质数来进行二次探测,进行(k)次迭代即可。
    取10-12个质数就足够稳了。

    代码21行是二次探测,25行是费马小定理。

    int ispr(ll n)
    {
        if(n<2)
            return 0;
        for(int i=0; i<12; i++)
            if(n%Pr[i]==0)
                return n==Pr[i];
        ll m=n-1,x,y;
        int k=0;
        while(~m&1)
        {
            m>>=1;
            k++;
        }
        for(int i=0; i<12; i++)
        {
            x=fpow(Pr[i],m,n);
            for(int j=0; j<k; j++)
            {
                y=Mul(x,x,n);
                if(y==1&&x!=1&&x!=n-1)
                    return 0;
                x=y;
            }
            if(x!=1)
                return 0;
        }
        return 1;
    }
    

    质因数分解 Pollard-rho

    先说一下为大整数 (N(Nle 10^{18})) 分解质因子的流程:

    (1)弄出来一个 (N) 的非平凡因子 (x)
    (2)递归分解 (x)(N/x)

    而Pollard-rho就是高效地寻找非平凡因子的算法。

    Pollard-rho基于随机化实现,然后算法的效率保证是“生日悖论”,这里的含义如下:

    如果随机一个数 (x),那么 (x)(N) 约数的概率极其微小。
    但是我们如果随机出 (x,y),那么 (|x-y|)(N) 约数的概率将显著提高。

    (1)不断随机数字(x),假如(gcd(x,n)>1)直接返回这个(gcd)

    额样例都跑不出来,没有任何前途。

    (2)按照上面讲的这么写,并按照倍增的形式不断调整:

    x=y=0;
    for(int k=2; ; k<<=1)
    {
        for(int i=1; i<=k; i++)
        {
            x=(x*x+sd)%n;
            v=gcd(abs(x-y),n);
            if(v!=1 && v!=n)
                return v;
            if(x==y)
                return n;
        }
        y=x;
    }
    

    效率玄学地大幅提升,但是依然TLE飞起。。。
    (3)然而你发现这个gcd常数巨大,我们把改成用一个模 (N) 意义下的变量记一下,这样每隔一会查一次就行!

    static int Size(1<<7);
    x=y=0;
    for(int k=2; ;k<<=1)
    {
        v=1;
        y=x;
        for(int i=1; i<=k; i++)
        {
            x=((u128)x*x+sd)%n;
            v=((u128)v*_abs(x-y))%n;
            if(i%Size==0)
            {
                z=gcd(v,n);
                if(z>1) return z;
            }
        }
        z=gcd(v,n);
        if(z>1) return z;
    }
    

    就能过了,常数极小。
    Pollard-rho时间复杂度是(O(n^{0.25}))
    实测每次找质因子内层乘法能稳定在(10^5)之内。
    其实感觉特别暴力。

  • 相关阅读:
    SQL数据库数据优化SQL优化总结( 百万级数据库优化方案)
    三星S8相机黑画面解决
    三星Galaxy S8 刷机经验记录
    2018.12.02 Socket编程之初识Socket
    工作至今
    巧用std::shared_ptr全局对象释放单例内存
    C++标准库之迭代器
    Phone 3rd Recovery
    进电机之两相双极性步进电机仿真
    使用Pretues仿真Arduino驱动步进电机
  • 原文地址:https://www.cnblogs.com/bestwyj/p/12122667.html
Copyright © 2020-2023  润新知