约数

约数是什么

(a=b*k)(k)为整数,则称(b)(a)的约数.

最大公约数

定义

两个整数的共有约数中最大的一个就是它们的最大公约数.(a)(b)的最大公约数用(gcd(a,b))表示.

算法

辗转相除法

又称欧几里得算法.
(a=k*b+r),(r equiv a pmod{b}),(lmid a)(lmid b).
( herefore r=a-k*b),(frac{r}{l}=frac{a}{l}-frac{k*b}{l})
(ecause lmid a,lmid b)
( herefore frac{r}{l})为整数
( herefore lmid r)
( herefore {a,b})的公约数与(b,a\%b)的公约数完全一致
( herefore gcd(a,b)=gcd(b,a\%b))
用这种方法不断递归,直到(a=b)时,返回(a)即可.

int gcd(long long a,long long b) {
    if(a==b) return a;
    if(a<b) swap(a,b);
    return gcd(b,a%b);
}

时间复杂度为(O(log {b})).

二进制算法

该算法一般用于高精度.

  1. (2mid a)(2mid b)时,(gcd(a,b)=2*gcd(frac{a}{2},frac{b}{2})).
  2. (2mid a)(2 mid b)时,(gcd(a,b)=gcd(frac{a}{2},b)).
  3. (2 mid a)(2 mid b)时,(gcd(a,b)=gcd(frac{a-b}{2},b)).

以下为高精度代码(压位).

#include<bits/stdc++.h>
#define w 18 //压位位数,2-18
using namespace std;
const long long len=pow(10,w);
struct data{
    long long num[10005],size;
}a,b;
void read(data &x) {
    char c[10005];
    int re[10005];
    memset(re,0,sizeof(re));
    cin>>c;
    for(int i=0; i<strlen(c); i++) {
        re[strlen(c)-i]=c[i]-'0';
    }
    x.size=0;
    for(int i=1;i<=strlen(c)/w+1;i++) {
		for(int j=0;j<w;j++) {
			x.num[i]*=10;
			x.num[i]+=re[w*i-j];
		}
        if(x.num[i]) x.size=i;
    }
}
void write(data x) {
    for(int i=x.size; i>0; i--) {
        if(i==x.size) {
            cout<<x.num[i];
            continue;
        }
        cout<<setw(w)<<setfill('0')<<x.num[i];
    }
}
void div2(data &x) {
    for(int i=x.size;i>0;i--) {
        if(x.num[i]&1) x.num[i-1]+=len;
        x.num[i]>>=1;
        if(!x.num[x.size]) x.size--;
    }
}
int div(data &x) {
    int cnt=0;
    while(!(x.num[1]&1)) div2(x),cnt++;
    return cnt;
}
void exc(data &x,data &y) {
    swap(x.size,y.size);
    for(int i=1;i<=max(x.size,y.size);i++) swap(x.num[i],y.num[i]);
}
bool change(data &x,data &y) {
    if(x.size>y.size) return false;
    if(x.size<y.size) {
        exc(x,y);
        return false;
    }
    for(int i=max(x.size,y.size);i>0;i--) {
        if(x.num[i]>y.num[i]) return false;
        if(x.num[i]<y.num[i]) {
            exc(x,y);
            return false;
        }
    }
    return true;
}
void time2(data &x) {
    for(int i=x.size;i>0;i--) {
        x.num[i]<<=1;
        if(x.num[i]>len-1) x.num[i]-=len,x.num[i+1]+=1;
    }
    int cnt=0;
    while(x.num[++cnt]) {
        if(x.num[cnt]>len-1) x.num[cnt]-=len,x.num[cnt+1]+=1;
    }
    if(x.num[x.size+1]) x.size++;
}
void time(data &x,int p) {
    for(int i=1;i<=p;i++) time2(x);
}
void reduce() {
    for(int i=1;i<=min(a.size,b.size);i++) {
        a.num[i]-=b.num[i];
        if(a.num[i]<0) a.num[i]+=len,a.num[i+1]-=1;
        if(!a.num[a.size]) a.size--;
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    read(a);
    read(b);
    int t1=div(a);
    int t2=div(b);
    int t=min(t1,t2);
    while(true) {
        if(change(a,b)) { 
            time(a,t);
            write(a);
            return 0;
        }
        reduce();
        div(a);
    }
}

相比二进制算法,时间复杂度一致,但常数更小.

不忘初心 砥砺前行
原文地址:https://www.cnblogs.com/gzezfisher/p/approximation.html