数论 质数 约数 概率与数学期望 博弈论:

定义: 不讲了qwq...

(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}))

费马小定理:

(a^p≡a(mod p)) 。

(a^p≡a(mod p)),p 也有很大概率为质数。

(a^p−1≡1(mod p)) 。

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

二次探测定理:

(k≡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}}})

(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

详情见上.


约数

(b|a).

算数基本定理的推论:

({P_{1}<P_{2}<P_{3}<...<P_{m}}))

((c_1+1)*(c_2+1)*(c_3+1)*...*(c_m+1)=prodlimits_{i=1}^m(c_i+1))

((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;
     }
}

(sqrt{N}))

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

(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(" ");
	}
}

(Nlog{N})).

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

(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两两互质.)

欧拉函数:

(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 $

(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不存在平局,只有先手必胜和先手必败的两种情况.

定理:

(A_1xorA_2xorA_3xor...xorA_n≠0)