数论

来源:互联网 发布:全国人口数据库 编辑:程序博客网 时间:2024/06/03 18:54

数论吐舌头

目录:

1、素数

  (1)暴力求解法

   (2)一般筛法

   (3)线性筛法

2、唯一分解定理

3、费马小定理

4、欧拉函数

5、欧几里得算法

  (1)递归

  (2)递推

6、扩展欧几里得

  (1)求同余方程

(2)求乘法逆元

  (3)求线性方程ax+by=c

7、快速幂

8、中国剩余定理

素数

    (1)暴力求解法

     根据素数的概念,没有1和其本身没有其他正因数的数。

所以只需枚举比这个数小的数,看能整除即可;

C++代码:

#include<iostream>

#include<cstdio>

#include<cmath>

using namespace std;

 

bool determine(int number)

{

if(n<=2)return false;

if(!n%2)return false;

     for(inti=3;i<=ceil(sqrt(number));i+=2)

//去掉了偶数的判断,效率提高一倍

     /*如果number整除以i,那么会得到两个的因数,

     而较小的那个因数不会超过number的二分之一次方;

     所以只需判断到number的平方根向上取整即可; */

         if(number%i);

         else return false;

     return true;

}

int main()

{

     int sum;

     cin>>sum;

     if(determine(sum))

         cout<<"YES!";

     elsecout<<"NO!";

     return 0;

}

时间复杂度:o(sqrt(n)/2);

空间复杂度:几乎没有;

(2)一般线性筛法:

  因为任何一个合数都能分解成几个素数相乘的形式;

   所以可以做一个表,首先把2设为质数,然后将2的倍数设为合数,剩下的数就是新得到的质数,然后重复这个过程,直到筛到合适的范围即可;

但是这个算法有缺陷:

1、       同一个数可能被筛多次,这就产生了多余的步骤。

2、       占用空间很大,如果使用bool数组的话,只能筛到1e9;

3、       从1-n筛,不能从m-n开始筛;

C++代码:

#include<cstring>

#include<cmath>

#include<iostream>

using namespace std;

 

bool s[1000000000];

int m,n;

 

int main()

{

     cin>>m>>n;

     memset(s,true,n);

     s[0]=s[1]=0;

     //输出M—N之间所有素数;

     for(inti=2;i<=ceil(sqrt(n));++i)

         if(s[i])

         {

              for(intj=i;j<=n;++j)

                   if(s[i*j])

                       s[i*j]=false;

         }

     for(int i=m;i<=n;++i)

         if(s[i])

              cout<<i<<'';

     return 0;

}

时间复杂度:o(n*loglogn);

空间复杂度:很大!注意数据大的话可能会爆空间;

(3)线性筛法求素数

这个占空间就更大了,需要使用一个bool数组和int数组

而亲身试验得到int数组最多开到1e8……

很无语,快确实是快了,但是测试数据一大,爆空间就更容易了;

#include<iostream>

#include<cstdio>

#include<cmath>

using namespace std;

 

int m,n,sum;

bool inp[1000000000];

int s[100000000]={0,0};

 

int main()

{

     cin>>m>>n;

     for(int i=2;i<=n;++i)

     {

         if(!inp[i])

              s[sum++]=i;

         for(intj=0;j<sum&&i*s[j]<=n;++j)

         {

              inp[i*s[j]]=true;

              if(!(i*s[j]))

                   break;

         }

     }

     for(int i=m;i<=n;++i)

         if(!inp[i])

              cout<<i<<'';

     return 0;

}

唯一分解定理

任何数都可以被唯一的分解成多个素数之积

例如:456=2*2*2*3*19;

C++代码:

#include<cstring>

#include<cmath>

#include<iostream>

#include<algorithm>

#include<cstdlib>

using namespace std;

 

bool s[1000000];

int m,n,sum=0,num;

int Prime[1212121];

int zhi[1500];

 

void Primes()

{

     for(int i=1;i<=num;++i)

         s[i]=true;

     s[0]=s[1]=0;

     for(int i=2;i<=num;++i)

         if(s[i])

         {

              Prime[++sum]=i;

              for(intj=i;j<=num;++j)

                   if(s[i*j])

                       s[i*j]=false;

         }

}

 

int main()

{

     int flag=0;

     cin>>num;

     int number=num;

     Primes();

     if(s[num])

     {

         cout<<num<<'='<<num;

         return 0;

     }

     cout<<num<<"=";str.chu();

     while(num>1)

     for(inti=1;num>1&&i<=sum;++i)

         if(!(num%Prime[i]))

         {

              zhi[++flag]=Prime[i];

              num/=Prime[i];

         }

     sort(zhi+1,zhi+flag+1);

     cout<<zhi[1];

     for(inti=2;i<=flag;++i)

         cout<<"*"<<zhi[i];

     return 0;

}

首先做一个质数表,并把质数存到数组里,然后用数模每个素数,如果为0则记录素数,最后排个序输出;

费马小定理

费马小定理:假如p是质数,且Gcd(a,p)=1,那么 a(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

利用这个性质可以判断数是否为素数

#include<cstdio>

#include<iostream>

usingnamespace std;

#defineLL long long

LL n,Prime[10]={2,3,5,7,11,13,17,19,23,29};

LL Mod(LLS,LL X)

{

    LL Num=0;

    while (S)

    {

        if (S&1)

          Num=(Num+X)%n;

        X=(X+X)%n;

        S>>=1;

    }

    return Num;

}

LLCount(LL S,LL X)

{

    LL Num=1;

    while (S)

    {

        if (S&1)

          Num=Mod(Num,X);

        X=Mod(X,X);

        S>>=1;

    }

    return Num;

}

boolCheck()

{

    if (n==2)

      return true;

    else

      if (n<2||!(n&1))

        return false;

    for (int a=0;a<10;a++)

    {

        if (n==Prime[a])

          return true;

        if (Count(n-1,Prime[a])!=1)

          return false;

    }

    return true;

}

intmain()

{

    scanf("%lld",&n);

    if (Check())

         cout<<"Yes";

    else

      cout<<"No";

    return 0;

}

欧拉函数

欧拉函数φ(n)为不大于n的与n互素的数的个数;

A与B互素,表示a与b的最大公约数为1,即(a,b)=1;

欧拉函数的符号φ读作fhi,在搜狗的特殊符号里可以找到;

,其中pi为x的质因数,其中φ(1)=1(唯一与1互质的数是1本身)

设n为正整数,以 φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值

φ:N→N,n→φ(n)称为欧拉函数。

几个性质(来自百度百科)

1、若n是质数p的k次幂, ,因为除了p的倍数外,其他数都跟n互质。

2、欧拉函数是积性函数——若m,n互质, 

3、特殊性质:当n为奇数时, ,证明与上述类似。

4、若n为质数则 

5、设p是素数,a是一个正整数,那么

C++实现:

#include<cstring>

#include<cmath>

#include<iostream>

#include<algorithm>

#include<cstdlib>

using namespace std;

 

bool s[1000000];

int m,n,sum=0,num;

int Prime[1212121];

int zhi[1500];

bool asd[1500];

int phi(int n)

{

    int i,rea=n;

    for(i=2;i*i<=n;i++)

    {

        if(n%i==0)

        {

            rea=rea-rea/i;

            while(n%i==0)  n/=i;

        }

    }

    if(n>1)

        rea=rea-rea/n;

    return rea;

}

 

void Primes()

{

     for(int i=1;i<=num;++i)

         s[i]=true;

     s[0]=s[1]=0;

     for(int i=2;i<=num;++i)

         if(s[i])

         {

              Prime[++sum]=i;

              for(intj=i;j<=num;++j)

                   if(s[i*j])

                       s[i*j]=false;

         }

}

 

int main()

{

     int flag=0;

     cin>>num;

     int number=num;

     Primes();

     if(num==1||!num)

     {

         cout<<"fhi"<<'('<<number<<")=";

         cout<<num;

         return 0;

     }

     if(s[num])

     {

         cout<<"fhi"<<'('<<number<<")="<<number-1;

         return 0;

     }

     while(num>1)

     for(inti=1;num>1&&i<=sum;++i)

         if(!(num%Prime[i]))

         {

              zhi[++flag]=Prime[i];

              num/=Prime[i];

         }

     int fenzi=1,fenmu=1;

     sort(zhi+1,zhi+flag+1);

     for(inti=1;i<=flag;++i)

         if(!asd[zhi[i]])

         {

              asd[zhi[i]]=true;

              fenzi*=zhi[i]-1;

              fenmu*=zhi[i];

         }

     cout<<"fhi("<<number<<")="<<number/fenmu*fenzi;

//cout<<"fhi("<<number<<")="<<fhi(number);

/*这是另一种求欧拉函数值的方法*/

     return 0;

}

欧几里得算法

辗转相除法,根据公式(a,b)=(b,r)

其中r为a%b,即a/b;

C++代码:

(1)递归

#include<iostream>

#include<cstdio>

using namespace std;

 

int GCD(int a,int b)

{

     if(a%b)

            return GCD(b,a%b);

     else return b;

}

 

int main()

{

     int a,b;

     cin>>a>>b;

     cout<<GCD(a,b);

     return 0;

}

(2)递推

#include<iostream>

using namespace std;

int main()

{

     int a,b,r;

     cin>>a>>b;

     r=m%n;

     while(r!=0)

     {

         a=b;

         b=r;

         r=m%n;

     }

     cout<<n;

     return 0;

}

扩展欧几里得

扩展欧几里得又称斐蜀定理,对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by;

求同余方程

#include<cstdio>

 

voidexgcb(int a,int b,int &x,int &y)

{

     if(!b)

     {

         x=1;y=0;

         return;

     }

     int q=a/b;

     int r=a%b;

     exgcb(b,r,y,x);

     y-=q*x;

}

intmain()

{

     int x,y;

     int a,b;

     scanf("%d %d",&a,&b);

     exgcb(a,b,x,y);

     while(x<0)

         x+=b;

     printf("%d",x);

     return 0;

}

求乘法逆元

#include<cstdio>

 

void exgcb(int a,int b,int &x,int &y)

{

     if(!b)

     {

         x=1;y=0;

         return;

     }

     int q=a/b;

     int r=a%b;

     exgcb(b,r,y,x);

     y-=q*x;

}

 

int Multiplicative inverse(int a,int b)

{

     int x,y;

     int gcb=GCD(a,b,x,y);

     if(1%gcb)return -1;

     x*=1%gcb;

     b=abs(b);

     int answer=x%b;

     while(answer<=0)

         answer+=b;

     return answer;

}

 

int main()

{

     int x,y;

     int a,b;

     scanf("%d%d",&a,&b);

     exgcb(a,b,x,y);

     while(x<0)

         x+=b;

     printf("%d\n",x);

     cout<<Multiplicativeinverse(a,b);

     return 0;

}

求线性方程ax+by=c

这个方程等同于ax≡c(mod b)

它有解的条件是(a,b)|c

方法一:脑子模拟,然后

#include<iostream>

using namespace std;

int main()

{

     int a,b,c;

     cin>>a>>b>>c;

     int r=c%a;

     c/=a;

     int k;

     b=0-b;

     int asd=a-r;

     c-=a;

     int sdf=c/(0-b);

     for(k=1;k<=sdf;++k)

     cout<<c+b*k<<''<<a*k+asd<<endl;

}

输出所有解;

方法二:

#include<cstdio>

#include<cstring>

#include<iostream>

using namespace std;

 

int a,b,c,x,y;

 

int exgcd(int a,int b,int &x,int &y)

{

     if(!b)

     {

         x=1;y=0;return a;

     }

     int e=exgcd(b,a%b,x,y);

     int kkz=x;x=y;y=kkz-a/b*y;

     return e;

}

 

int main()

{

     scanf("%d%d%d",&a,&b,&c);

     int k=exgcd(a,b,x,y);

     if(c%k)printf("Impossible\n");

     else

     {

         k=c/k;

         x*=k;y*=k;

         printf("x=%d,y=%d\n",x,y);

     }

     return 0;

}

快速幂

递归与非递归

#include<iostream>

using namespace std;

 

int quick(int,int,int);

int double_quick(int ,int ,int);

 

int main()

{

     inta,b,c;//求a^b modc;

     cin>>a>>b>>c;

     cout<<double_quick(a,b,c);

     cout<<endl<<quick(a,b,c);

}

int quick(int a,int b,int c) 

     intans=1;

   if(b==1) return a; 

   ans=quick(a,b>>1,c)%c; 

   if(b&1) return ans*ans*a%c; 

   else return ans*ans%c; 

}

 

int double_quick(int a,int b,int c)

{

   int ans=1,base=a;

   while(b!=0)

     {

       if(b&1!=0)

              ans*=base%c;

       base*=base%c;

       b>>=1;

     }

   return ans%c;

}

中国剩余定理(孙子定理)

#include<iostream>

using namespace std;

void exgcd(int a,int b,int &x,int &y)

{

     if(!b)

     {

         x=1;y=0;

         return;

     }

     int q=a/b;

     int r=a%b;

     exgcd(b,r,y,x);

     y-=q*x;

}

int CRT(int a[],int m[],int n)

{

    int M = 1;

    int ans = 0;

    for(int i=1; i<=n; i++)

        M *= m[i];

    for(int i=1; i<=n; i++)

    {

        int x, y;

        int Mi = M / m[i];

        exgcd(Mi,m[i],x,y);

        ans = (ans + Mi * x *a[i]) % M;

    }

    if(ans < 0) ans += M;

    return ans;

}

int main()

{

     int n,mi;

     int a[5555],m[5555];

     cin>>n;

     mi=n;

     while(n--)

         cin>>a[n]>>m[n];

     cout<<CRT(a,m,mi);

}

 

0 0
原创粉丝点击