数论基础模板

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数论基础模板相关的知识,希望对你有一定的参考价值。

以前写的几个数论模板,整整吧,反正数论这么弱貌似只会gcd的样子...

来,先来gcd

1.技术分享
#include<iostream>
#include<cstdio>

using namespace std;

int gcd(int a,int b)
{
    if(b==0) return a;
    else return gcd(b,a%b);
}

int lcm(int x,int y)
{
    return (x*y)/gcd(x,y);
}
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%d %d",gcd(a,b),lcm(a,b));
    return 0;
}
gcd与lcm

 

扩展欧几里得

求解 x,y的方法的理解
设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,a>b>0 时
设 ax1+ by1= gcd(a,b);
bx2+ (a mod b)y2= gcd(b,a mod b);
根据朴素的欧几里得原理有 gcd(a,b) = gcd(b,a mod b);
则:ax1+ by1= bx2+ (a mod b)y2;
即:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);
根据恒等定理得:x1=y2; y1=x2- [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
 
2.技术分享ex-gcd
技术分享
/*
扩展欧几里得 
ax%b==1  -> ax-by==1 
求不定方程的一组解 使x为最小正整数解 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int x,y,gcd;
int Extend(int a,int b)
{
    if(b==0)
      {
          x=1;y=0;
          gcd=a;
      }
    else 
      {
          Extend(b,a%b);
          int tmp=x;
          x=y;
          y=tmp-a/b*y;
      }
}
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    Extend(a,-b);
    x=x*(1/gcd);
    if(x<0)while(x<0)x=x+b;
    else while(x-b>0)x=x-b;
    printf("%d\\n",x);
    return 0;
}
codevs 同余方程

 

质因数分解

这个吗 一般算法的复杂度是够用的
当然如果筛素数的话会快 而且对于一个质数 可以直接返回
当然如果对n!每个数质因数分解的话那筛素数就快多了
当然我们还有别的方法 忘记叫什么名字了 咨询的数竞的孩子
n!里p的个数(p为素数)=n/p+n/(p*p)+n/(p*p*p)+n/(p*p*p*p)...(p*p*..)<=n
这样就快得多了 有时排列组合可以用到这个

3.技术分享
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n;
int a[10000];

int main()
{
    scanf("%d",&n);
    printf("%d=",n);
    int cnt=0;
    for (int i=2;i<=n;i++)
    {
        while (n%i==0&&n)
        {
            a[cnt++]=i;
            n/=i;
        }
    }
    for (int i=0;i<cnt-1;i++)
    {
        printf("%d*",a[i]);
    }
    printf("%d\\n",a[cnt-1]);
    return 0;
} 
分解单个数
4.技术分享
#include<iostream>
#include<cstdio>
#define maxn 5000010
#define mod 100000007
#define ll long long
using namespace std;
ll n,prime[maxn/10],c[maxn/10],num,ans=1;
bool f[maxn]; 

void Prime()
{
    for(int i=2;i<=n;i++)
    {
        if(f[i]==0)prime[++num]=i;
        for(int j=1;j<=num;j++){
            if(i*prime[j]>maxn-10)break;
            f[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
void Solve(){
    for(int i=1;i<=num;i++){
        ll P=prime[i];
        if(P>n)break;
        while(n>=P){
            c[i]+=n/P;P*=prime[i];
        }
        if(c[i]) printf("%d ",c[i]);
    }
}

int main()
{
    cin>>n;
    Prime();Solve();
    return 0;
}
阶乘分解

 

快速幂

5.技术分享
#include<iostream>
#include<cstdio>
#define mod 1000000007

using namespace std;
int x,y;

int main()
{
    scanf("%d%d",&x,&y);
    int r=1;
    while(y)
    {
        if(y&1) r=(long long)r*x%mod;//y&1 pan duan zhe yi wei shi fou shi yi
        y>>=1;x=(long long)x*x%mod;
    }
    printf("%d",r%mod);
    return 0;
}
快速幂取模

 

欧拉函数
定义:设n为正整数 不大于n的且与n互素的整数个数记为f(n) 
求欧拉函数的方法
首先暴力好打 慢
f(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)... pi为n的质因子 
那就要分解质因数了 好办
应用嘛 还是比较好使的
求逆元
定义应用
a^(f(p))%p=1

6.技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll n,ans;
ll init()
{
    ll x=0;char s=getchar();
    while(s<0||s>9)s=getchar();
    while(s>=0&&s<=9){x=x*10+s-0;s=getchar();}
    return x;
}
int main()
{
    while(1)
      {
          n=init();
          if(n==0)break;
          ans=n;
        for(ll i=2;i*i<=n;i++)
          if(n%i==0)
              {
                while(n%i==0)n/=i;
                ans=ans/i*(i-1);
            }
        if(n>1)ans=ans/n*(n-1);
        printf("%ld\\n",ans);
      }
    return 0;
}
欧拉函数

 

素数

7.技术分享
#include<cstdio>
#define maxn 22000
using namespace std;
int l,r,cnt,num,prime[maxn];
bool f[maxn];
void Oprime(){
    for(int i=2;i<=maxn;i++){
        if(f[i]==0)prime[++num]=i;
        for(int j=1;j<=num;j++){
            if(i*prime[j]>maxn)break;
            f[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
int main()
{
    scanf("%d%d",&l,&r);
    Oprime();
    for(int i=1;i<=num;i++){
        if(prime[i]>=l&&prime[i]<=r)cnt++;
        if(prime[i]>r)break;
    }
    printf("%d\\n",cnt);
    return 0;
}
欧拉筛

素数判定

(1)费马小定理

复杂度(次数*logn) 
若p为素数 则a^(p-1)%p=1
但是如果翻过来就不一定正确
优异类数叫做卡迈克尔数就是合数
但上面那个概率是很大的所以嘛
多生成几个a试试 一般出错的概率还是很小的
当然如果考试那天人品XX的话就怨不得谁了

8.技术分享
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#define ll long long

using namespace std;
ll p,flag;

ll mul(ll a,ll b)
{
    a%=p;b%=p;
    ll r=0;
    while(b)
    {
        if(b&1)
        {
            b--;r+=a;r%=p;
        }
        a<<=1;a%=p;b>>=1;
    }
    return r%p;
}

ll qm(ll a,ll b)
{
    ll r=1;
    while(b)
    {
        if(b&1) r=mul(r,a);
        b>>=1; a=mul(a,a);
    }
    return r;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>p;
    if(p==1)
    {
        printf("No\\n");
        return 0;
    }
    if(p==2)
    {
        printf("Yes\\n");
        return 0;
    }
    srand(time(0));
    for(int i=1;i<=15;i++)
    {
        int a=rand()%(p-1)+1;
        int x=qm(a,p-1);
        if(x!=1)
        {
            flag=1;
            break;
        }
    }
    if(flag)
    {
        printf("No");
        return 0;
    }
    else
    {
        printf("Yes");
        return 0;
    }
    return 0;
}
费马小定理

(2)Miller Rabin(摘抄峰峰的,我不会)

复杂度(logn)好像还有个常熟 不过无伤大雅
再说说误判概率 经过k轮这玩意 将合数误判为素数的概率是(1/4)^k
素数误判为合数的概率是0 一般进行个10轮15轮就好了
要是这样的概率都给碰上了那就没啥可说的了....
下面说说算法过程
首先 2特判一下子 对于剩下的奇素数
有 fermat a^(p-1)%p=1
避免出错同时加速这个算法
另一个定理 若x&1,且x*x%p==1 则有x=1或x=-1(即x==p-1)
把p-1写成p-1=m*2^j 然后呢 j次测试 若不满足上面那个东西 直接肯定p不是prime 
每次都乘回来 最后又得到了p-1 再来一次fermat就ok了

9.技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define ll long long
using namespace std;
ll p;
ll mul(ll a,ll b){
    a%=p;b%=p;
    ll r=0;
    while(b){
        if(b&1){
            b--;r+=a;r%=p;
        }
        a<<=1;a%=p;b>>=1;
    }
    return r%p;
}
ll qm(ll a,ll b){
    ll r=1;
    while(b){
        if(b&1)r=mul(r,a);
        b>>=1;a=mul(a,a);
    }
    return r;
}
bool Miller_Rabin(){
    if(p<=2)return 1;
    if(p%2==0)return 0;
    ll x,y,m,k=0,T=15,a;
    m=p-1;
    while(m%2==0){
        k++;m>>=1;
    }
    while(T--){
        a=rand()%(p-1)+1;
        x=qm(a,m);
        for(ll i=1;i<=k;i++){
            y=mul(x,x);
            if(y==1&&x!=1&&x!=p-1)return 0;
            x=y;
        }
        if(x!=1)return 0;
    }
    return 1;
}
int main()
{
    cin>>p;
    if(Miller_Rabin())printf("Yes\\n");
    else printf("No\\n");
    return 0;
}
Miller Rabin

 

表示还很弱很弱很弱,不会的还有很多很多很多.......

 

 

以上是关于数论基础模板的主要内容,如果未能解决你的问题,请参考以下文章

[vscode]--HTML代码片段(基础版,reactvuejquery)

十个html5代码片段,超实用,一定要收藏

PAT 数列的片段和简单数论

算法模板:数论之质数全家桶(内含埃氏筛法,欧拉线性筛法详解)沈七

数论模板总结 -- 未完待续

VSCode自定义代码片段——.vue文件的模板