• 数论


    总纲:

    1.筛法求素数

    2.欧几里德算法

    3.扩展欧几里德

    4.最小公倍数

    5.斐波那契

    6.快速幂

    7.唯一分解定理

    8.中国剩余定理

    9.欧拉函数

    1.筛法求素数:

    用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛至n结束。

    注意:最小的素数为2,所以筛时应注意从2开始

    过程举例:

    1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 19 20

    21 22 23 24 25 26 27 28 29 30

    1不是素数,去掉。剩下的数中2最小,是素数,去掉2的倍数,余下的数是:

    3 5 7 9 11 13 15 17 19 21 23 25 27 29

    剩下的数中3最小,是素数,去掉3的倍数,如此下去直到所有的数都被筛完,求出的素数为:

    2 3 5 7 11 13 17 19 23 29

    代码:

     1 #include<iostream>
     3 #include<cstdio>
     5 #include<cmath>
     7 using namespace std;
     9 int vis[1000];
    11 int main()
    13 {
    15     int n;
    17     cin>>n;
    19     for(int i=2;i<=sqrt(n)+1;i++)
    21      {
    23         if(vis[i]==0)
    25          {
    27             for(int j=i*2;j<=n;j+=i)//从i*2开始筛,i--2*i由i之前筛去
    29              {
    31                 vis[j]=1;
    33               }
    35          }
    37      }
    39      for(int i=2;i<=n;i++) 
    41       {
    43         if(vis[i]==0)
    45          { 
    47             cout<<i<<" "; 
    49           }
    51       } 
    53 }

    1453 统计素数个数 2

    时间限制: 1 s

    空间限制: 256000 KB

    题目等级 : 黄金 Gold

    题目描述 Description

    判断[a,b]中素数的个数

    输入描述 Input Description

    输入共1行,a,b两数

    输出描述 Output Description

    输出共1行,输出素数的个数

    样例输入 Sample Input

    3 5

    样例输出 Sample Output

    2

    数据范围及提示 Data Size & Hint

    对于100%的数据,a,b≤5000,000

     

     1 #include<iostream>
     2  #include<cstdio>
     3  #include<cmath>
     4   using namespace std;
     5   const int N=5000000;
     6   int vis[N];
     7   int main()
     8   {
     9       int l,r;
    10      cin>>l>>r;
    11      vis[0]=1;
    12      vis[1]=1;
    13      for(int i=2;i<=sqrt(r)+1;i++)
    14       {
    15           if(vis[i]==0)
    16            {
    17                for(int j=i*2;j<=r;j+=i)
    18                 {
    19                     vis[j]=1;
    20                  }
    21            }
    22       }
    23       int ans=0;
    24       for(int i=l;i<=r;i++)
    25        {
    26            if(vis[i]==0)
    27             {
    28                 ans++;
    29             }
    30        }
    31        cout<<ans;
    32  }

    ——————————————————————————————————

    ——————————————————————————————————

    2.欧几里德辗转相除求最大公约数:

    gcd函数的基本性质:

    gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)

    辗转相除法举例:

    若a<b,则a/b=0(余a);

    求(319,377):

    ∵ 319÷377=0(余319)

    ∴(319,377)=(377,319);

    ∵ 377÷319=1(余58)

    ∴(377,319)=(319,58);

    ∵ 319÷58=5(余29)

    ∴ (319,58)=(58,29);

    ∵ 58÷29=2(余0)

    ∴ (58,29)= 29;

    ∴ (319,377)=29。

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int gcd(int x,int y)
    {
        return x%y==0?y:gcd(y%x,x);
    }
    int main()
    {
            int n,m;
            scanf("%d%d",&n,&m);
            printf("%d
    ",gcd(n,m));
         }
    }

    ——————————————————————————————————

    3.扩展欧几里德

    扩展欧几里德求解同余方程的解

    例解 ax≡1mod(b);

    给出a,b;

    求x的最小值;

    求上述等价于 ax+by=1,x,y,属于Z;

    有欧几里德辗转相除可知

    gcd(a,b)=gcd(b,a%b);
    
    所以 当b==0是,a就是gcd(a,b)的值;
    
    也就是 此时 gcd(a,b)=a*1+b*0==a;
    
    即原式中x==1,y==0;
    
    设  gdc(a,b)=a*x1+b*y1
    
    gcd(b,a%b=b*x2+a%b*y2
    
    ∵gcd(a,b) =gcd(b,a%b)
    
    ∴a*x1+b*y1=b*x2+a%b*y2
    
    化简上式可得
    
    a*x1+b*y1=b*x2+a%b*y2
    
    a*x1+b*y1=b*x2+(a-a/b*b)*y2
    
    a*x1+b*y1=a*y2+b*x2-a/b*b*y2
    
    a*x1+b*y1=a*y2+b*(x2-a/b*y2)
    
    则此时
    
    x1=y2  y1=(x2-a/b*y2)

    所以递归式就出来了;

    代码:

     1 #include <cstdio>
     2 int a, b;
     3 int exgcd(int a,int b,int &x,int &y)
     4 {
     5     if(b==0)
     6     {
     7         x=1,y=0;
     8         return a;
     9     }
    10     int r=exgcd(b,a%b,x,y),tmp;
    11     tmp=x,x=y,y=tmp-a/b*y;
    12     //x1=y2 ;
    13     //y1= x2-a/b*y2;
    14     return r;
    15 }
    16 int main()
    17 {
    18     scanf("%d%d", &a, &b);
    19     int x, y;
    20     exgcd(a, b, x, y);
    21     while (x < 0) x = x + b;//最小正整数解
    22     printf("%d", x);
    23     return 0;
    24 }

    ——————————————————————————————————

    _____________________________________________________________________

    4.最大公约数

    N,m最大公约数就是用a*b/gcd(a,b);

    代码:

    int lcm(int x,int y)

    {

        return x*y/gcd(x,y);

    }

    _____________________________________________________________________

    5.菲波那切数列

    0 1 1 2 3 5 8 13 21 34 55 89 144……

    递归的定义为:F[0]=0,F[1]=1,F[2]=1,F[3]=2,F[4]=3……

    F[n]=F[n-1]+F[n-2];

    斐波那契数列通项公式的证明:

    <1>.公式法

    求斐波那契的N项的通项公式为:

    ;

    代码:(注意:除ans用long long类型外都用double类型)

    用long long类型最多算到第92项;

    用int 类型最多算到第46项;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 using namespace std;
     5 const double a=(1+sqrt((double)5))/2;
     6 const double b=(1-sqrt((double)5))/2;
     7 int main()
     8 {
     9     long long c;
    10     cin>>c;
    11     double n=sqrt(5);
    12     double m=pow(a,c)-pow(b,c);
    13     long long ans=1/n*m;
    14     cout<<ans;
    15 }

    <2>.递归法:

    注意:递归适用于层数较小的结果算到第38项时时间差不多到1s;

    代码:

     1 #include<iostream>
     2 
     3 #include<cstdio>
     4 
     5 using namespace std;
     6 
     7 int FB(int n)
     8 
     9 {
    10 
    11     if(n==1||n==0)return 1;//固定值
    12 
    13     else return FB(n-1)+FB(n-2);//递归公式
    14 
    15 }
    16 
    17 int main()
    18 
    19 {
    20 
    21     int n;
    22 
    23     cin>>n;
    24 
    25     int ans=FB(n);
    26 
    27     cout<<ans;
    28 
    29 }
    30 
    31 <3>.递推法:
    32 
    33 注意:用地推法数组F[]开long long类型最多算到第95项;
    34 
    35 首先另f[1]=0;f[2]=1;//此处根据需要赋f[1],f[2]的初值;
    36 
    37 然后运用公式f[n]=f[n-1]+f[n-2]求得;
    38 
    39 代码:
    40 
    41 #include<iostream>
    42 
    43 #include<cstdio>
    44 
    45 using namespace std;
    46 
    47 const int N=1000;
    48 
    49 long long f[N];
    50 
    51 int main()
    52 
    53 {
    54 
    55     int n;
    56 
    57     cin>>n;
    58 
    59     f[1]=0;
    60 
    61     f[2]=1;
    62 
    63     for(int i=3;i<=n;i++)
    64 
    65      {
    66 
    67         f[i]=f[i-1]+f[i-2];
    68 
    69      }
    70 
    71      cout<<f[n];
    72 
    73 }

    例题:

    2834 斐波那契数

    时间限制: 1 s

    空间限制: 128000 KB

    题目等级 : 黄金 Gold

     

    题目描述 Description

    小X是个聪明的孩子,他记得斐波那契数列f(n)中前1000个数。不过由于学业的压力,他无法记得每一个数在数列中的位置。

    他现在知道斐波那契数列中的一个数f(x)模P后的值N(即f(x) mod P=N)以及x可能的最大值M,如果再对于斐波那契数列中每一个数都模P,他想知道这个数可能出现在第几个。不过小X还要做作业呢,这个问题就交给你由编程来解决了。

    输入描述 Input Description

    一行,共3个整数,第一个数为N,第二个数为P,第三个数为x可能的最大值M,三个数以空格隔开。

    输出描述 Output Description

    一个整数,满足f(i) mod P = N的最小的i,如果不存在则输出-1。

    样例输入 Sample Input

    3 7 5

    样例输出 Sample Output

    4

    数据范围及提示 Data Size & Hint

    对于20%的数据,保证0<M≤50

    对于50%的数据,保证0<M≤100

    对于70%的数据,保证0<M≤500

    对于100%的数据,保证0<M≤1000,0≤N<P,P为素数且2<P<105。

     1 #include<iostream>
     2 
     3 #include<cstdio>
     4 
     5 using namespace std;
     6 
     7 const int N=1000;
     8 
     9 int f[N];
    10 
    11 int main()
    12 
    13 {
    14 
    15     int n,p,m;
    16 
    17     cin>>n>>p>>m;
    18 
    19     f[1]=1;
    20 
    21     f[2]=1;
    22 
    23     if(n==1){cout<<"1";return 0;}//tepan
    24 
    25     for(int i=3;i<=m;i++)
    26 
    27      {
    28 
    29         f[i]=(f[i-1]+f[i-2])%p;
    30 
    31         if(f[i]==n)
    32 
    33          {
    34 
    35             cout<<i;
    36 
    37            return 0;
    38 
    39          }
    40 
    41      }
    42 
    43      cout<<"-1";
    44 
    45      return 0;
    46 
    47 }

    __________________________________________________________________________________________________________________________________________

    5.快速幂

    进制转换代码:

     1 #include<iostream>
     2 
     3 #include<cstdio>
     4 
     5 using namespace std;
     6 
     7 int a[1000];
     8 
     9 int main()
    10 
    11 {
    12 
    13     int n;
    14 
    15     cin>>n;
    16 
    17     int j=0;
    18 
    19     int    i=n;
    20 
    21     while(i!=0)
    22 
    23      {
    24 
    25         a[++j]=i%2;//2进制
    26 
    27         i/=2;//2进制
    28 
    29      }
    30 
    31      for(int i=j;i>=1;i--)
    32 
    33       {
    34 
    35         cout<<a[i];
    36 
    37       }
    38 
    39 }

    快速幂就是快速算底数的n次幂

    求a^n

    将n转换为2进制数;从其2进制数的低位到高位将其拆成a^n0*a^n1*a^n2……;

    例如2^11;

    11的2进制数为1011;

    则 2^11=2^3*1+2^2*0+2^1*1+2^0+1;

    代码:

     1 #include<iostream>
     2 
     3 #include<cstdio>
     4 
     5 using namespace std;
     6 
     7 int pow2(int a,int b)
     8 
     9 {
    10 
    11     int r=1,base=a;
    12 
    13     while(b!=0)//将其转化为2进制数   1位1位列举
    14 
    15        {
    16 
    17     if(ans==1) r*=base;//b%2==1说明此数2进制最后一位不为0;
    18 
    19     base*=base;//此数累乘;
    20 
    21 b/=2;//将其2进制最后一位向前移一;
    22 
    23 //b>>1;
    24 
    25     }
    26 
    27     return r;
    28 
    29 }
    30 
    31 int main()
    32 
    33 {
    34 
    35    int n,m;
    36 
    37    cin>>n>>m;
    38 
    39    cout<<pow2(n,m);     
    40 
    41 }

    __________________________________________________________________________________________________________________________________________

    6.唯一分解定理

    存在性证明:

    设   M = P1 * P2 * … * Pr = Q1 * Q2 * … * Qs

    P1 <= P2 <= ... <= Pr, Q1 <= Q2 <= ... <= Qs。

    P1是不等于Q1的,不然两边同时约掉它,我们就得到一个更小的有两种分解方法的数

    不妨设P1 < Q1,那么我们用P1替换掉等式最右边中的Q1,得到一个比M更小的数T = P1 * Q2 * Q3 * ... * Qs令M' = M - TM' = (P1 * P2 * ... * Pr) - (P1 * Q2 * ... * Qs) = P1 * (P2 * .. * Pr - Q2 * ... * Qs)  ……  (1)    M' = (Q1 * Q2 * ... * Qs) - (P1 * Q2 * ... * Qs) = (Q1 - P1) * Q2 * ... * Qs  ………………  (2)     由于T比M小,因此M'是正整数。从(1)式中我们立即看到,P1是M'的一个质因子。注意到M'比M小,因此它的质因数分解方式应该是唯一的,可知P1也应该出现在表达式(2)中既然P1比所有的Q都要小,因此它不可能恰好是(2)式中的某个Q,于是只可能被包含在因子(Q1-P1)里。但这就意味着,(Q1-P1)/P1除得尽,也就是说Q1/P1-1是一个整数,这样Q1/P1也必须得是整数。我们立即看出,P1必须也是Q1的一个因子,这与Q1是质数矛盾了。这说明,我们最初的假设是错误的。

    求解n的分解

    代码:

     1 #include <iostream>   
     2 
     3 #include<cstdio>
     4 
     5 using namespace std;   
     6 
     7 int main()   
     8 
     9 {   
    10 
    11     int i,a[10000],c,n,t;   
    12 
    13     scanf("%d",&t);   
    14 
    15     while(t--)   
    16 
    17     {   
    18 
    19         scanf("%d",&n);   
    20 
    21         c=0;   
    22 
    23         for(i=2;i<=n;i++)   
    24 
    25         {  
    26 
    27                   int ans=n%i;
    28 
    29             while(ans==0)   
    30 
    31             {   
    32 
    33                 a[c++]=i;   
    34 
    35                 n/=i;
    36 
    37                             ans=n%i;  
    38 
    39             }   
    40 
    41         }   
    42 
    43         for(i=0;i<c;i++)   
    44 
    45         {   
    46 
    47             printf(i==0?"%d":"*%d",a[i]);   
    48 
    49         }   
    50 
    51         printf("
    ");   
    52 
    53     }   
    54 
    55     return 0;   
    56 
    57 }

    __________________________________________________________________________________________________________________________________________

    7.中国剩余定理

    8.欧拉函数

  • 相关阅读:
    SignalR + MVC5 简单示例
    SignalR 简单示例
    Web API 简单示例
    Windows Azure 使用体验
    SQL Server 2014 安装小记
    SSRS 迁移
    SSH配置(同一服务器不同用户)
    【6】Zookeeper脚本及API
    【3】Kafka安装及部署
    【2】Kafka概念及原理
  • 原文地址:https://www.cnblogs.com/lyqlyq/p/6769649.html
Copyright © 2020-2023  润新知