31.1 基础数论概念
先简要回顾一下书中内容:
整除性与约数:d|a 表示为d整除a,存在整数k,使得a=kd
若d≥0,则称d是a的约数。
素数与合数:素数:如果能被平凡约数1和自身整除即为素数。
合数:如果整数a>1且不是素数,则称之为合数。
除法定理,余数和等模
除法定理: 对于任何整数a和任何正整数n,存在唯一整数q和r,满足0≤r<n且a=qn+r,称q=(向下取整)(a/n)为除法的商,值r=a mod n为除法的余数。
等模:对于整数模n的余数,可以划分为n个等价类。包含整数a的模n等价类为[a]n={a+kn:k∈Z}
公约数与最大公约数:
公约数:d|a,a|b,则d是a与b的公约数。
最大公约数:两个不同时为0的整数a与b的公约数中最大的称为最大公约数。记做gcd(a,b).
相关定理:1.若任意整数a和b不都为0,则gcd(a,b)是a与b的线性组合集{ax+by:x,y∈Z}中的最小正元素。
2.对任意整数a与b,如果d|a且d|b,,则d|gcd(a,b).
3.对所有整数a与b以及任意非负整数n.有gcd(an,bn)=ngcd(a,b)
4.对于任意正整数n,a,b .如果n|ab且gcd(a,n)=1,则n|b。
互质数:如果两个数的只有公约数1,则a与b称为互质数。
相关定理:5.对于任意整数a,b和p,如果gcd(a,p)=1且gcd(b,p)=1则 gcd(ab,p)=1.
唯一因子分解定理
相关定理:1.对所有素数p和所有整数a,b,如果p|ab,则p|a或p|b(或者两者都成立)。
2.合数a技能以一种方法写成如下乘积形式:a=p(e1,1)p(e2,2)....p(er,r) 其中pi为素数,p1<p2<...<pr,且er为正整数。
31.1-1 证明:若a>b>0,且c=a+b,则c mod a=b.
设c mod a=x,则存在整数k,则c=ak+x=a+b 若k=1,则x=b,若k≠1 ,由于a>b>0,c=a+b>0 ,k=向下取整(c/a)>0.所以k≥2,所以ak+x=a+b≥2a+x 所以b≥a+x≥a 这样与题目假设矛盾。
所以k=1,x=b,c mod a=b.
31.1-2证明有无穷多个素数。(提示:证明素数p1,p2....,pk都不能整除(p1p2....pk)+1)
首先证明提示部分:假设pi(i=1,2,...k)能整除(p1p2....pk)+1),则(p1p2....pk)+1)=k*pi=>pi(k-p1p2..p(i-1)...p(i+1)...pk)=1 pi=1/(k-p1p2..p(i-1)...p(i+1)...pk)>1所以k-p1p2..p(i-1)...p(i+1)...pk<1又因为pi(k-p1p2..p(i-1)...p(i+1)...pk)=1>0且pi>0 所以(k-p1p2..p(i-1)...p(i+1)...pk)>0 所以k-1<(p1p2..p(i-1)...p(i+1)...pk)<k ,又因为(p1p2..p(i-1)...p(i+1)...pk)为整数,但是它在(k-1,k)这个没有整数的区间里,所以与假设矛盾。
现在证明原题:假设只有有限个素数,p1,p2...pk.则(p1p2....pk)+1)比p1,p2...pk有限个素数都大的数是合数。但是由提示知:p1,p2....,pk任何一个素数都不能整除(p1p2....pk)+1
根据定理31.8。由于合数能分解成一组素数,那么(p1p2....pk)+1也不能被合数整除,所以(p1p2....pk)+1不能被任何除1和它本身的数整除,所以(p1p2....pk)+1是素数。得证!
31.1-3 证明:如果a|b且b|c,则a|c.
a|b =>存在整数k1,b=k1*a. 同理 存在整数k2,c=k2*b所以c=k2*b=k2*(k1*a)=(k1*k2)*a 所以a|c
31.1-4 证明:如果p是素数并且0<k<p,则gcd(k,p)=1.
若k为素数,因为0<k<p且p是素数,由于k的约数为1和k,p的约数为1和p,因为k≠p,所以两个素数只有1为公约数,所以gcd(k,p)=1.
若k为合数,因为0<k<p且p是素数,由于k的约数是1和数个小于k的数,而p的约数为1和p,因为k的所有约数都小于p,所以gcd(k,p)=1.
31.1-5 证明:对于任意正整数n,a,b .如果n|ab且gcd(a,n)=1,则n|b。
因为n|ab,所以存在整数k,使得ab=kn ,gcd(a,n)=1 =>gcd(a,ab/k)=1 =>kgcd(a,ab/k)=k =>gcd(ak,ab)=k => a*gcd(k,b)=k
所以ab=kn => ab= a*gcd(k,b)n => b=gcd(k,b)n => n|b
31.1-6 证明:如果p是素数且0<k<p,则.证明对所有整数a,b和素数p,有(a+b)^p≡a^p+b^p(mod p).
是组合数,所以是整数,所以k!(p-k)!|p!,p是素数且对于任意i∈(1,p-1),有gcd(p,i)=1 所以gcd(p,k!(p-k)!)=1(这里有个定理我不得不说,因为书上没有相关定理,gcd(m,a)=1,则有gcd(ab,m)=gcd(b,m))则对于任意k∈[1,p-1],k!(p-k)!)|(p-1)!(这里也有个定理,需要特别说明,gcd(m,a)=1,m|ab,则m|b)。所以存在整数z,使(p-1)!=z*k!(p-k)!.两边同乘以p,则有p!=zk!(p-k)!p => p!/(k!(p-k)!)=zp =>p| p!/(k!(p-k)!)=>
下面证明第二个问题 因为所以所以存在整数k,使(a+b)^p=a^p+b^p+kp. 因为b^p+kp≡ b^p(mod p),所以可证结论。
31.1-7证明:如果a和b是任意正整数,且满足a|b,则对任意x,(x mod b) mod a= xmod a 在相同的假设下,证明对任意整数x和y,若果x≡y(mod b),则x≡y(mod a).
设x mod b=y则存在整数k1,使得x=bk1+y.设y mod a=z,则存在整数k2使得y=ak2+z 所以x=bk1+ak2+z 又因为a|b,则存在整数k. b=ak 所以x=(ak)k1+ak2+z=a(kk1+k2)+z所以x mod a=z=y mod a=(x mod b) mod a.
在相同假设下,x≡y(mod b) =>存在整数k1使得y≡bk1+x. 又因为a|b,则存在整数k. b=ak 使得y≡a(kk1)+x,所以x≡y(mod a).
31.1-8 对任意整数k>0,如果存在一个整数a,满足a^k=n,则称整数n是一个k次幂。如果对于某个整数k>1,n>1是一个k次幂,则称n是非平凡幂。说明如何在关于β的多项式时间内判定一个β位整数n是否是非平凡幂。
以下是代码:
//最朴素的多项式时间内判断一个数是否为某个数的幂的形式:就是用枚举法,挨个找,但是这个是关于n的多项式,关于β的多项式暂时没有想出。
#include <iostream>
#include <math.h>
using namespace std;
void main()
{
int n=64,flag=1;
//O(√nlgn)
for (int i=2;i<=sqrt(n);i++)//O(a=√n)
{
int m=n,k=0;
while (m%i==0)//O(k=lgn)
{
m=m/i;
k++;
}
if (m==1&&k>1)
{
cout<<n<<"是非平凡幂,存在一个整数"<<i<<"它的"<<k<<"次幂="<<n<<endl;
flag=0;
break;
}
}
if (flag)
{
cout<<"n="<<n<<"不存在非平凡幂"<<endl;
}
}
31.1-9证明等式(31.6)-31.10
需要证明的等式已用加粗
31.6 gcd(a,b)=gcd(b,a) 根据整数的交换率便知
31.7 设d是a与b的约数,则d|a,d|b.存在整数k有a=dk =>|a|=d(±k) =>d||a|,所以d|(±a),可见d|(-a)又因为d|b且-a与b的所有约数都一样,那么最大约数也一样故gcd(-a,b)=gcd(a,b)
31.8 根据31.7知:设d是a与b的约数,若d|a=>d||a| 同理d|b=>d||b| ,所以d是|a|与|b|的约数,既然所有约数都一样,那么最大的约数当然也一样了。gcd(a,b)=gcd(|a|,|b|)得证!
31.9设d是a的约数,则显然d也是0的约数(因为任何整数乘以0都得0,)所以d也是a与0的公约数,从中找出最大的,a的最大约数为它本身也就是a(当然a>0)所以a=gcd(a,0),若a<0,-a=gcd(-a,0)=gcd(a,0)(根据31.7) 所以对于任意整数a,有|a|=gcd(|a|,0)=gcd(a,0)(根据31.8)
31.10 若a>0,则 gcd(a,ka)=a*gcd(1,k)(根据推论31.4),又因为任意整数与1的最大公约数肯定是1,gcd(a,ka)=a
若a<0,则 gcd(a,ka)=gcd(-a,-ka)=(-a)*gcd(1,k)(根据推论31.4),又因为任意整数与1的最大公约数肯定是1,gcd(a,ka)=-a
若a=0,则gcd(0,0)=0,所以gcd(a,ka)=a 总结:gcd(a,ka)=|a|
31.1-10 证明:最大公约数运算满足结合律,即证明对所有整数a,b和c。gcd(a,gcd(b,c))=gcd(gcd(a,b),c)
在证明之前,先证明:gcd(a,b,c)=gcd(a,gcd(b,c))
设d=gcd(a,gcd(b,c)),则d|a,d|gcd(b,c).所以d|a,d|b,d|c.所以d是a,b,c的约数,所以gcd(a,b,c)≥gcd(a,gcd(b,c))。。。。(1)
设d'=gcd(a,b,c).则d'|a,d'|b,d'|c 由推论31.3知d'|gcd(b,c) 又因为d'|a,所以再次使用d'|gcd(a,gcd(b,c)) .因为最大公约数一定是正整数,所以存在正整数k,使得gcd(a,gcd(b,c))=kd'
d'≤gcd(a,gcd(b,c)) 即gcd(a,b,c)≤gcd(a,gcd(b,c)).。。.。。(2) 由(1)与(2)知gcd(a,b,c)=gcd(a,gcd(b,c)) 同理可证:gcd(a,b,c)=gcd(gcd(a,b),c)
所以gcd(a,b,c)=gcd(a,gcd(b,c))=gcd(gcd(a,b),c) 。
31.2-11 证明定理31.8
注:这个定理的证明可参考北师大《初等数论》第6讲,里面有详细解答。这里略过。
31.1-12 试写出计算β位整除除以短整数的高效算法,以及计算β位整数除以短整数的余数的高效算法。所给出的算法运行时间应为θ(β^2).(感觉31.1-12和31.1-13应该用FFT算法解决。)
既然用高效的算法,那就用位运算。
//位运算的乘法与除法
#include <iostream>
using namespace std;
//位运算的乘法
int bit_Multiplication(int a,int b)
{
int ans=0;
for (int i=1;i;i<<=1,a<<=1)
{
if (b&i)
{
ans+=a;
}
}
return ans;
}
//位运算的除法
int bit_Division1(int x,int y)
{
int ans=0;
for (int i=31;i>=0;i--)
{
if ((x>>i)>=y)
{
ans+=(1<<i);
x-=(y<<i);
}
}
return ans;
}
//计算整数的二进制位数
int bit_num(int d)
{
int i=0;
while (d)
{
d>>=1;
i++;
}
return i;
}
//位运算的除法 计算商
int bit_Division2_quotient(int x,int y)
{
int c2=bit_num(x),c1=bit_num(y),quotient=0;
for (int i=c2-c1;i>=0;i--)//i=c2-c1防止除数y移位后超过无符号整数最大值 时间复杂度O(c2-c1)
{
unsigned int a=(y<<i);//有了i=c2-c1保证了y<<i不会溢出 a有c1+c2-c1=c2位
if (a<=x)
{
quotient+=(1<<i);
x-=a;
}
}
//总的时间复杂度为 O(c2)=O(x的二进制位数)=O(b^2) b为除数的十进制位数
return quotient;
}
//位运算的除法 计算余数 与计算商一样,只是返回值不同
int bit_Division2_Remainder(int x,int y)
{
int c2=bit_num(x),c1=bit_num(y),quotient=0;
for (int i=c2-c1;i>=0;i--)//i=c2-c1防止除数y移位后超过无符号整数最大值 时间复杂度O(c2-c1)
{
unsigned int a=(y<<i);//有了i=c2-c1保证了y<<i不会溢出 a有c1+c2-c1=c2位
if (a<=x)
{
quotient+=(1<<i);
x-=a;
}
}
//总的时间复杂度为 O(c2)=O(x的二进制位数)=O(b^2) b为除数的十进制位数
return x;
}
void main()
{
cout<<bit_Multiplication(350,43)<<endl;
cout<<bit_Division1(350,43)<<endl;
cout<<"商:"<<bit_Division2_quotient(350,43)<<endl;
cout<<"余数:"<<bit_Division2_Remainder(350,43)<<endl;
}
31.1-13 写出一个高效算法,用于将β位二进制整数转化为响应的十进制表示。证明:如果长度至多为β的整数的乘法或除法运算所需时间为M(β),则执行二进制到十进制转换所需时间为θ(M(β)lgβ)。(提示:应用分治法,分别使用独立的递归计算结果的前段和后段)(感觉31.1-12和31.1-13应该用FFT算法解决。)
这次要用位运算+分治思想。我对我写的这段代码持怀疑态度。如果牛人看到实际没用到分治思想,那么给出理由和比较好的建议。谢谢!
//用分治思想进行进制转换(2->10),写得不好,凑合看吧
/*#include <iostream>
#include <string>
using namespace std;
#define BIT 6//二进制整数的位数,可根据所要输入的二进制位数设置BIT。
int t=-1;
int Bit_merge(int a[],int p,int r)
{
static ans=0;
p=(p>t)?p:(t+1);
for (int i=p;i<=r;i++)
{
t++;
ans+=a[i]<<i;
}
return ans;
}
int bit_Multiplication(int a[],int p,int r,int flag)//x为二进制数
{
int q;
if (p<r)
{
q=(p+r)/2;
bit_Multiplication(a,p,q,0);
bit_Multiplication(a,q+1,r,1);
}
if (p<=r&&t!=BIT)return flag==0?Bit_merge(a,p,q):Bit_merge(a,q+1,r);
}
void main()
{
int a[BIT]={0};
string x;
cin>>x;
int j=0;
while (j!=BIT)
{
a[j]=x[BIT-1-j]-'0';
j++;
}
cout<<endl;
cout<<bit_Multiplication(a,0,BIT-1,0)<<endl;
}
如果β位整数的乘法或除法的运行时间为M(β),那么用分治法的递归式为T(β)=2T(β/2)+θ(M(β))=>T(β)=θ(M(β)lgβ)
31.2最大公约数
欧几里得递归算法:
定理31.9(GCD递归定理)对任意非负整数a和任意正整数b. gcd(a,b)=gcd(b,a mod b)
引理31.10 如果a>b≥0,并且EUCLID(a,b)执行了k≥1次递归调用,则a≥F(k+2),b≥F(k+1)
定理31.11(Lame定理) 对任意整数k≥1,如果a>b≥1,且b<F(k+1),则EUCLID(a,b)的递归调用次数少于k次。
代码如下:
//欧几里得算法递归形式
#include <iostream>
using namespace std;
int Euclid(int a,int b)
{
cout<<"gcd("<<a<<","<<b<<")=";
if (b==0)
{
return a;
}
else
{
return Euclid(b,a%b);
}
}
void main()
{
cout<<Euclid(30,21)<<endl;
}
欧几里得算法扩展形式
代码如下:
#include <iostream>
using namespace std;
struct a
{
int d,x,y;
}s;
struct a extended_eucild(int a,int b)
{
if(b==0)
{
s.d=a,s.x=1,s.y=0;
return s;
}
else
{
struct a ss=extended_eucild(b,a%b);
s.d=ss.d;
s.x=ss.y;
s.y=ss.x-(a/b)*ss.y;
return s;
}
}
int Fibonacci(int n)
{
if (n==1)
{
return 1;
}
else if (n==0)
{
return 0;
}
else
{
return Fibonacci(n-2)+Fibonacci(n-1);
}
}
void main()
{
struct a s=extended_eucild(99,78);
cout<<s.d<<" "<<s.x<<" "<<s.y<<endl;
}
<strong><span style="font-size:18px;color:#000000;">31.2-1 证明:由式(31.11)和式(31.12)可推得式(31.13)</span></strong>
31.2-2计算调用过程EXTENDED-EUCLID(899,493)的返回值为(d,x,y).
a | b | 向下取整a/b | d | x | y |
899 | 493 | 1 | 29 | -6 | 11 |
493 | 406 | 1 | 29 | 5 | -6 |
406 | 87 | 4 | 29 | -1 | 5 |
87 | 58 | 1 | 29 | 1 | -1 |
58 | 29 | 2 | 29 | 0 | 1 |
29 | 0 | 一 | 29 | 1 | 0 |
31.2-3 证明:对所有整数a,k和n,gcd(a,n)=gcd(a+kn,n)
gcd(a,n)=gcd(n,a mod n)=gcd(a mod n,n) 对所有整数a,k',n和x,设a mod n=x,则a=nk'+x=>x=a-nk,所以gcd(a,n)=gcd(a-nk',n),因为k'为所有整数,则令k=-k'
则gcd(a,n)=gcd(a+nk,n).
31.2-4仅用常数大小的存储空间(即仅存储常数个整数值)把过程EUCLID改写成迭代形式。
代码如下:
//欧几里得算法迭代形式
#include <iostream>
using namespace std;
int Euclid(int a,int b)
{
while (b)
{
int c=b;
b=a%b;
a=c;
}
return a;
}
void main()
{
cout<<Euclid(89,55)<<endl;
}
31.2-5 如果a>b≥0,证明:EUCLID(a,b)至多执行1+logb次递归调用。把这个界改进为1+log(b/gcd(a,b)).
改进的界我暂时不会。
31.2-6 过程EXTENDED-EUCLID(F(k+1),Fk)返回什么值?证明答案的正确性。