10.1.1欧几里得算法和唯一分解定理
除法表达式
分析:
表达式的值一定可以写成A/B的形式:A是其中一些Xi的乘积,而B是其他数的乘积,而B是其他数的乘积。不难发现,X2必须放在分母位置,那其他数呢?
其他数均可以放在分子位置
接下来的问题就变成了:判断E是否为整数
第一种方法是利用前面介绍的高精度运算:k次乘法加一次除法,正确但是麻烦
第2种方法是利用唯一分解定理,把X2写成若干素数相乘的形式:
第3种方法是直接约分:每次约掉Xi和X2的最大公约数gcd(Xi,X2),则当且仅当约分结束后X2=1时E为整数,程序如下
int judge(int* X){
X[2]/=gcd(X[2],X[1]);
for(int i=3;i<=k;i++)
X[2]/=gcd(X[i],X[2]);
return X[2]==1;
}
整个算法的时间效率取决于这里的gcd算法。
辗转相处法的关键在于如下恒等式:gcd(a,b)=gcd(b,a mod b).她和边界条件gcd(a,0)=a一起构成了下面的程序:
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
利用gcd还可以求出两个整数a,b的最小公倍数lcm(a,b)
不难验证gcd(a,b)*lcm(a,b)=a*b,不过即使有了公式也不要大意,如果把lcm写成a*b/gcd(a,b),可能会出错,因为a*b会溢出,正确的写法是先除后乘,即a/gcd(a,b)*b
10.1.2Eratosthenes筛法
无平方因子的数。给出正整数n,m,区间[n,m]内的“无平方因子”的数有多少个?
分析:
对于这样的限制,直接枚举判断会超时:需要判断10***0个整数,所以需要用Eratosthenes筛法构造1~n的素数表
筛法的思想特别简单:对于不超过n的每个非负整数p,删除2p,3p,,,当处理完所有数之后,还没被删的就是素数,用VIS[i]表示i已经被删除
memset(vis,0,sizeof vis);
for(int i=2;i<=n;i++)
for(j=i*2;j<=n;j+=i)
vis[j]=1;
10.1.3扩展欧几里得算法
直线上的点。求直线ax+by+c=0上有多少个整点,满足x [x1,x2],y [y1,y2]
分析:
在解决这个问题之前,首先学习欧几里得算法——找出一对整数(x,y),使得ax+by=gcd(a,b).注意,这里x和y不一定是正数,也可能是负数或者0.例如,gcd(6.15)=3,6*3-15*1=3,其中x=3,y=-1.这个方程还有其他解,如x=-2,y=1
下面是扩展欧几里得算法的程序
void gcd(int a,int b,int& d,int& x,int& y)
{
if(!b)
{
d=a;x=1;y=0;
}
else
{
gcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
用数学归纳法并不难证明算法的正确性,注意在递归调用时,x和y的顺序变了,而边界也是不难得出的:gcd(a,0)=1*a-0*0=a.这样,唯一需要记忆的是y-=x*(a/b),不懂也不要紧。
上面求出了ax+by=gcd(a,b)的一组解(x1,y1),?其他解?任取另外一组解(x2,y2),则ax1+by1=ax2+by2(他们都等于gcd(a,b)),变形得a(x1-x2)=b(y2-y1),近一些列的变形得出
他的任意数解都可以写成(x0+kB,y0-kA),A=a/gcd(a,b),B=b/gcd(a,b),k取任意整数
。。。。。。
10.1.4同与与模算术
可得下面公式
(a+b)mod n=((a mod n)+(b mod n))mod n
(a-b)mod n=((amodn)-(bmodn)+n)mod n
注意在减法中,由于a mod n可能小于b mod n,需要在结尾上+n,以及乘法过程中可能会溢出
int mul_mod(int a,int b,int n){
a%=n; b%=n;
return (int)((long long)a*b%n);
}
分析:
首先,把大数写成自左向右的形式:1234=((1*10+2)*10+3)*10+4
scanf("%s%d",n,&m);
int len=strlen(n);
int ans=0;
for(int i=0;i<len;i++)
ans=(int)(((long long)ans*10+n[i]-'0')%m);
printf("%d\n",ans);
这个函数的时间复杂度为n,当n很大时速度不理想
下利用分治法
int pow_mod(int a,int n,int m){
if(n==0)
return 1;
int x=pow_mod(a,n/2,m);
long long ans=(long long)x*x%m;
if(n%2==1)
ans=ans*a%m;
return (int)ans;
}
//不是很懂