• 数论


    质数

    定义: 不讲了qwq...

    性质: 对于一个足够大的整数N,不超过N的质数大约有(N lnN) 个.

    试除法:

    *若一个正整数N为合数,则存在一个能整除N的数T,其中(2≤T≤sqrt{N})

    inline bool int prime(int n)
    {
        for(RN i=2;i<=sqrt(n);i++)
            {
                if(n%i==0)
                    return false;
            }
        return true;
    }
    

    Miller-Robbin:

    1.问题描述:

    *测试一个正整数 p 是否为素数,需要较快且准确的测试方法。((p≤10^{18}))

    费马小定理:

    对于质数 p 和任意整数 a ,有 (a^p≡a(mod p))

    反之,若满足 (a^p≡a(mod p)),p 也有很大概率为质数。

    将两边同时约去一个 a ,则有 (a^p−1≡1(mod p))

    也即是说:假设我们要测试 n 是否为质数。我们可以随机选取一个数 a ,然后计算 (a^n−1(mod n)) ,如果结果不为 1 ,我们可以 100% 断定 n 不是质数。否则我们再随机选取一个新的数 a 进行测试。如此反复多次,如果每次结果都是 1 ,我们就假定 n 是质数.

    二次探测定理:

    如果 p 是奇素数,则 (k^2≡1(mod p)) 的解为 k≡1 或 (k≡p−1(mod p))

    即如果当我们用一个a费马小n后,看n是不是偶数,如果是,则取(dfrac{n}{2}),根据二次探测定理可知,(dfrac{n}{2})即为k,则再判断(n^2≡p−1(mod p))(n^2≡1(mod p))是否正确.若不成立,则不是质数.如果n是奇数,则直接跳过二次探测定理,用下一个a来费马.

    几个 ai 的值: 2,3,5,7,11,61,13,17。用了这几个 ai,就连那个被称为强伪素数的 46856248255981 都能被除去.

    单次测试失败的几率为 (dfrac{1}{4}) 。那么假设我们做了 times 次测试,那么我们总错误的几率为 ((dfrac{1}{4})^{time})如果 times 为 20 次,那么错误概率约为 0.00000000000090949470 也就是意味着不可能发生的事件。
    单次时间复杂度是 O(logn) ,总复杂度就是 O(times(log{n})) 了。注意 long long 会爆,用 int128 就行了。

    代码实现:

    #define ll long long
    ll p,a[]={2,3,5,7,11,61,13,17};
    inline ll mul(ll a,ll b,ll mod)
    {
        ll ans=0;
        for(ll i=b;i;i>>=1)
        {
            if(i&1) ans=(ans+a)%p;
            a=(a<<1)%p;
        }
        return ans%p;
    }
    inline ll Pow(ll a,ll b,ll p)
    {
        ll ans=1;
        for(ll i=b;i;i>>=1)
        {
            if(i&1) ans=mul(ans,a,p);
            a=mul(a,a,p);
        }
        return ans%p;
    }
    inline bool Miller_Robbin(ll p)
    {
        if(p==2) return true;
        if(p==1 || !(p%2)) return false;
        ll d=p-1;int s=0;
        while(!(d&1)) d>>=1,++s;
        for(int i=0;i<=8 && a[i]<p;++i)
        {
            ll x=Pow(a[i],d,p),y=0;
            for(int j=0;j<s;++j)
            {
                y=mul(x,x,p);
                if(y==1 && x!=1 && x!=(p-1)) return false;
                x=y;
            }
            if(y!=1) return false;
        }
        return true;
    }
    

    Pollard-Rho 大合数分解:

    算法流程:

    先判断当前数N是否是素数(Miller_Rabin)了,如果是则直接返回。如果不是素数的话,试图找到当前数的一个因子(可以不是质因子)。然后递归对该因子和约去这个因子的另一个因子进行分解。

    算法解决:

    那么,如何找到这个数的一个因子呢?对于一个大整数n,采用一种随机化算法,我们取任意一个数x使得x是n的质因数的几率很小,但如果取两个数x1以及x2使得它们的差是n的因数的几率就提高了.理论支撑:

    生日悖论:

    假设一年有 N 天,那么只要 $n≥sqrt{N} $存在相同生日的概率就已经大于 50% 了.即假如一个班级有58位同学,那么必定有两个同学的出生日期是相同的.正确性证明

    利用伪随机数判断:

    根据生日悖论,我们可以生成很多随机数相减法,与要求的N找最大公约数, 假设枚举的两个数为 i,j ,假设 i>j ,那么判断一下 gcd(i−j,n) 是多少,如果非 1 ,那么我们就已经找出了一个 n 的因子了,这样的概率随着枚举的组数变多会越来越大。但如果我们一开始就把所有的随机数都造出来,不仅内存储存不下,而且效率也不高,所以就有了以下生成伪随机函数:

    f(x)=(x2+a)modn
    inline int find_factorplus(int N) {
        a = 2;
        b = a;
        do {
            a = f(a);//a runs once
            b = f(f(b));//b runs twice as fast
            p = GCD( abs( b - a ) , N);
            if( p > 1 ) return p;//Found factor: p
        } while( b != a );
        return 0;//Failed. 
    }
    

    一开始只需要两个数a,b,而且a可以rand也可以自己取(例如2),b可以根据随机函数生成.然而一般这种简单的随机数都会出现循环节,我们需要判断一下循环节.

    Floyd判环:

    两个人同时在一个环形跑道上起跑,如果一个人是另外一个人的两倍速度,那么这个人绝对会追上另外一个人。追上时,意味着其中一个人已经跑了一圈了。

    根据上面的结论,我们可以把b变成2倍速度a,如果发现有环,即a==b,那么跳出循环.寻找另外一个a.

    inline int find_factorplus(int N) {
        a = 2;
        b = a;
        do {
            a = f(a);//a runs once
            b = f(f(b));//b runs twice as fast
            p = GCD( abs( b - a ) , N);
            if( p > 1 ) return p;//Found factor: p
        } while( b != a );
        return 0;//Failed. 
    }
    

    Miller-Rabin 优化:

    这个算法对于因子多,因子值小的数 n 是较为优异的。

    也就是对于它的一个小因子 p , Pollard−Rho 期望在 O((sqrt{p})) 时间内找出来。
    但对于 n 是一个质数的情况,就没有什么较好的方法快速判断了,那么复杂度就是 O((sqrt{p})) 的。
    此时就退化成暴力试除法了。
    此时我们考虑用前面学习的 Miller−Rabin 算法来优化这个过程就行了。
    代码实现:

    #include<bits/stdc++.h>
    using namespace std;
    #define RI register int
    #define LL long long
    inline LL read()
    {
        LL res=0,f=1;
        char ch=getchar();
        while(ch!='-'&&(ch>'9'||ch<'0'))
            ch=getchar();
        if(ch=='-')
            f=-1,ch=getchar();
        while(ch>='0'&&ch<='9')
            res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
        return res*f;
    }
    LL ans;
    LL ksm(int x,LL p,LL Mod)
    {
        if(p==1)
            return x%Mod;
        LL res=ksm(x,p>>1,Mod);
        res=(__int128)res*res%Mod;
        if(p&1)
            res=(__int128)res*x%Mod;
        return res%Mod;
    }
    LL gcd(LL x,LL y)
    {
        return y?gcd(y,x%y):x;
    }
    inline bool mr0(int x,LL p)
    {
        if(ksm(x,p-1,p)!=1)
            return false;
        LL k=p-1;
        while(!(k%2))
        {
            k>>=1;
            LL t=(ksm(x,k,p)+p)%p;
            if(t!=1&&t!=p-1)
                return false;
            if(t==p-1)
                return true;
        }
        return true;
    }
    inline bool mr(LL x)
    {
        if(x==46856248255981||x<2)
            return false;
        if(x==2||x==3||x==7||x==61||x==24251)
            return true;
        return mr0(2,x)&&mr0(3,x)&&mr0(7,x)&&mr0(61,x)&&mr0(24251,x);
    }
    inline LL find(LL x)
    {
        LL t1=rand()%x,c=rand()%x,t2=((__int128)t1*t1+c)%x;;
        LL dif=abs(t1-t2),cnt=0;
        __int128 G=gcd(dif,x);
        if(G>1)
            return G;
        while(true)
        {
            t1=((__int128)t1*t1+c)%x;
            t2=((__int128)t2*t2+c)%x;
            t2=((__int128)t2*t2+c)%x;
            if(t1==t2)
                return gcd(G,x);
            dif=abs(t1-t2);
            if(G*dif%x==0)
                return gcd(G,x);
            G=G*dif%x;
            if(cnt%127==0)
            {
                LL r=gcd(G,x);
                if(r>1)
                    return r;
                cnt=1;
            }
            cnt++;
        }
    }
    inline void PollardRho(LL x)
    {
        if(x==1||x<ans)
            return;
        if(mr(x))
        {
            if(x>ans)
                ans=x;
            return;
        }
        LL y=x;
        while(y==x)
            y=find(x);
        PollardRho(y);
        PollardRho(x/y);
    }
    int main()
    {
        srand((unsigned)time(NULL));
        int T=read();
        while(T--)
        {
            LL x=read();
            ans=1;
            if(mr(x))
            {
                printf("Prime
    ");
                continue;
            }
            PollardRho(x);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    参考博客

    质数的筛选:

    给定一个整数N,求出1~N之间的所有质数.

    Eratosthenes 筛法:

    任意整数x的倍数都不是质数.
    时间复杂度O((NloglogN))

    inline void prime(int x)
    {
        memset(v,o,sizeof(v));
        for(RN i=1;i<=n;i++)
            {
                if(v[i]) continue;
                cout<<i<<endl;
                for(RN j=i;j<=n/i;j++)
                    v[i*j]=1;
             }
    }
    

    线性筛法:

    从大到下累计质因子,让每一个数只有一种质因子产生方式
    设数组V[]记录每个数的最小质因子.

    1.依次考虑2~N之间每一个数i.

    2.若v[i]=i,说明i是质数,把它保存下来.

    3.扫描不大于v[i]的每个质数P,令v[i∗p]=p.也就是说在i的基础上累计一个质因子P,因为(pleqslant v[i]),所以p便是(p*i)的最小质因子.这样便能找出每一个数唯一的质因数分解方式.
    时间复杂度O(N).
    代码实现:

    int v[maxn],prime[maxn];
    inline void find(int n)
    {
        memset(v,0,sizeof(v));
        for(RN i=2;i<=n;i++)
            {
                if(v[i])
                    continue;
                cout<<i<<endl;
                for(RN j=i;j<=n/i;j++)
                    v[i*j]=1;
            }
    }
    

    质因数分解:

    基本定理:

    任何一个大于1的正整数都能唯一分解为有限个质数的乘积.

    (color{BlueViolet}{N=P_{1}^{c_{1}}*P_{22}^{c_{2}}*...P_{m}^{c_{m}}})

    其中(c_{i})都是正整数,(p_{i})都是质数,且满足(p_{1}<p_{2}<...<p{m}).

    方法1 试除法:

    inline void find(int n)
    {
    	memset(c,0,sizeof(c));
    	m=0;
    	for(RN i=2;i*i<=n;i++)
    	{
    		if(n%i==0)
    			{
    				p[++m]=i;
    				while(n%i==0)
    					n/=i,c[m]++;
    			}
    	}
    	if(n>1)
    		{
    			p[++m]=n;
    			c[m]=1;
    		}
    	for(RN i=1;i<=m;i++)
    		{
    			cout<<p[i]<<"^"<<c[i]<<endl;
    		}
    }
    

    方法2 Pollard-Rho

    详情见上.


    约数

    约数,又称因数。整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。记为(b|a).

    算数基本定理的推论:

    已知(color{BlueViolet}{N=P_{1}^{c_{1}}*P_{22}^{c_{2}}*...P_{m}^{c_{m}}})(({P_{1}<P_{2}<P_{3}<...<P_{m}}))

    N的正约数个数为 ((c_1+1)*(c_2+1)*(c_3+1)*...*(c_m+1)=prodlimits_{i=1}^m(c_i+1))

    N的所有正约数之和为 ((1+p_1+p_1^{2}+...+p_1^{c_1})*...*(1+p_m+p_m^{2}+...+p_m^{c_m}=prodlimits_{i=1}^m(sum_{j=0}^{c_i}(p_i)^j))

    试除法——求N的正约数集合:

    int f[1600],m=0;
    for(RN i=1;i*i<=n;i++)
    {
        if(n%i==0)
        {
            f[++m]=i;
            if(i!=n/i) f[++m]=n/i;
         }
    }
    

    时间复杂度为O( (sqrt{N}))

    (color{BlueViolet}{试除法推论}):

    一个整数N的约数个数上界为(2sqrt{N}).

    倍数法——求出1~N每个数的正约数集合:

    inline void find(int n)
    {
    	vector<int> f[50010];
    	for(RN i=1;i<=n;i++)
    		for(RN j=1;j<=n/i;j++)
    		{
    			f[i*j].push_back(i);
    		}
    	for(RN i=1;i<=n;i++)
    	{
    		int x=f[i].size();
    		for(RN j=0;j<x;j++)
    			printf("%d ", f[i][j]);
    		puts(" ");
    	}
    }
    

    时间复杂度O((Nlog{N})).

    (color{BlueViolet}{倍数法推论}):

    1~N的约数个数的总和大约为为(Nlog{N}).

    最大公约数:

    定义:最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。a,b的最大公约数记为gcd(a,b),同样的,a,b,c的最大公约数记为gcd(a,b,c),多个整数的最大公约数也有同样的记号。求最大公约数有多种方法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。与最大公约数相对应的概念是最小公倍数,a,b的最小公倍数记为lcm[a,b].

    定理:

    (forall a,bsubset{N},gcd(a,b)*lcm(a,b)=a*b).

    求法:

    更相减损术 :

    (forall a,bsubset{N},有 gcd(a,b)=gcd(b,a-b)=gcd(a,a-b))

    (forall a,bsubset{N},有 gcd(2a,2b)=2gcd(a,b))

    (color{DeepPink}{ ext{适用于高精度算法}})

    欧几里得算法 :

    (forall a,bsubset{N},b eq 0, gcd(a,b)=gcd(b,a mod b))

    inline int gcd(int a,int b)
    {
        return b? gcd(a,a%b):a;
    }
    

    时间复杂度O((log(a+b)))

    互质与欧拉函数

    定义:

    (forall a,b,subset{N},若 gcd(a,b)=1,则称a,b互质.)

    (若gcd(a,b)=gcd(b,c)=gcd(a,c)=1,则称a,b,c两两互质.)

    欧拉函数:

    1~N中于N互质的数的个数被称为欧拉函数,记为(varphi(N))

    (varphi(N)=N*prodlimits_{(prime)P|N}(1-frac{1}{p}))

    根据计算公式,可以在分解质因数的时候求出欧拉函数.

    int get_phi(int x){
        int re=x;
        for(int i=2;i*i<=x;i++)
            if(x%i==0){
                re/=i;re*=i-1;
                while(x%i==0)
                    x/=i;
            }
        if(x>1) re/=x,re*=x-1;//最后一个.
        return re;
    }
    

    时间复杂度O((sqrt{N})).

    欧拉函数性质:

    ((1).forall n>1 ,1sim n) 中与n互质的数的和为 (n imesfrac{varphi(n)}{2}.)

    ((2).)若a,b互质,则(varphi(ab)=varphi(a) imesvarphi(b).)

    ((3).)(n)为奇数时,(varphi(2n)=varphi(n).)

    ((4).)(pmid n),且(p^2mid n),则(varphi(n)=varphi(frac{n}{p}) imes p.)

    ((5).)(pmid n),且(p^2 mid n),则(varphi(n)=varphi(frac{n}{p}) imes (p-1).)

    ((6).)若p为质数,则(varphi(p)=p-1.)

    ((7).)$sum_{dmid n}varphi(d)=n $

    由此我们便可以通过线性筛在O(n)的时间内算出(2sim N)中每一个数的欧拉函数.

    inline void find(int n)
    {
    	memset(v,0,sizeof(v));
    	int m=0;
    	for(RN i=2;i<=n;i++)
    	{
    		if(!v[i])
    		{
    			v[i]=i,p[++m]=i;
    			phi[i]=i-1;
    		}
    		for(RN j=1;j<=m;j++)
    		{
    			if(p[j]>v[i]||p[j]>n/i)
    				break;
    			v[i*p[j]]=p[j];
    			phi[i*p[j]]=phi[i]*(i%p[j] ? p[j]-1 : p[j]);
    		}
    	}
    }
    

    以及Eratosthenes筛法:

    inline void find(int n)
    {
        for(RN i=2;i<=n;i++)
        phi[i]=i;
        for(RN i=2;i<=n;i++)
            if(phi[i]==i)
                for(int j=i;j<=n;j+=i)
                    phi[j]=phi[j]/i*(i-1);
    }
    

    时间复杂度O((N log{N}))

    概率与数学期望

    样本点:随机实验的某种可能结果.

    样本空间:所有可能结果所形成的集合.

    随机事件:

    随机变量:

    概率:

    定义:

    博弈论:

    NIM博弈

    局面:游戏中面临的状态.

    先手:整局游戏第一个行动的人.

    后手:整局游戏第二个行动的人.

    必败局面:若在某一局面下无论采取何种行动, 都会输掉游戏的局面.

    必胜局面/最优策略:若在某一局面存在某种行动,使行动后对手面临必败局面.

    NIM不存在平局,只有先手必胜和先手必败的两种情况.

    定理:

    NIM博弈先手必胜,当且仅当(A_1xorA_2xorA_3xor...xorA_n≠0)

  • 相关阅读:
    CMDXP.CMD命令大全
    ASP.NET 页生命周期概述
    使用 ASP.NET 2.0提供的WebResource管理资源
    软件包管理 之 Fedora / Redhat 软件包管理指南
    开源代码分析研究 之 BugNet (2008年1月14日更新 第一章BugNet 简介 已完成)
    软件包管理 之 如何编译安装源码包软件
    硬件管理 之 Linux 硬件管理的基础知识
    软件包管理 之 fedorarpmdevtools 工具介绍
    软件包管理 之 Fedora/Redhat 在线安装更新软件包,yum 篇 ── 给新手指南
    系统引导管理 之 系统引导过程及硬盘分区结构论述
  • 原文地址:https://www.cnblogs.com/lvhuanzhu/p/10902033.html
Copyright © 2020-2023  润新知