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;
}
//不是很懂