算法导论第三十一(31)章数论算法

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的最大公约数肯定是1gcd(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)返回什么值?证明答案的正确性。


                
  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值