数论

总纲:

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\n",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("\n");   
52 
53     }   
54 
55     return 0;   
56 
57 }

 

 

__________________________________________________________________________________________________________________________________________

7.中国剩余定理

8.欧拉函数

posted @ 2017-04-26 17:21  ioioioioioio  阅读(452)  评论(0编辑  收藏  举报