基础篇——数论基础

来源:互联网 发布:头戴式蓝牙推荐 知乎 编辑:程序博客网 时间:2024/06/07 01:37

数论基础主要有三点。1.最大公约数。2.素数问题。3.快速幂取模。

一:最大公约数

正常的思路,求最大公约数都是从2开始到n-1求最大能整除的数即为最大公约数。但这样算的时间为o(n),相当耗时。故在求最大公约数时,提出了辗转相除法。

现有a,b两个数,a除以b的商和余数分别是p和q,所以a=b*p+q,所以gcd( b , q )整除a,b故而整除gcd( a ,b )。反之因为q=a-b*q,可得gcd( a,b )整除gcd( b,q )。因此gcd( a,b )=gcd( b,a%b )。由于gcd的第二个参数是不断减小的,最终会得到gcd( a,b )=gcd( c,0 )。0和c的最大公约数是c,故c为a和b的最大公约数。

辗转相除法是一个递归的算法,其时间复杂度为o(log max(a,b)),大多数情况下够用了。

int gcd(int a,int b){    if(b==0) return a;    gcd(b,a%b);}

二:素数问题

素数也称质数,指除了1和其本身外再无别的因数的数。一般判定一个数是否是素数,通常我们会从2到√n遍历,若有能整除n的数则说明n不是素数。算法时间为o(√n),看起爱还可以,但若是给你一串数求其中素数的个数,那么这种算法就显得low了。有人机智的提出了素数筛。

将2到n之间的整数记录下来,其中最小的素数是2,将表中2的倍数全部不划去,表中剩余的最小的数字为3,再将3的倍数全划去,以此类推,就能找到所有的素数。这样做的时间复杂度只有o(nlog log n)。

int prime[MAXN+1];    //用于记录每个素数bool is_prime[MAXN+1];//值为true即为素数int sieve(int n){    int p=0;    for(int i=1;i<=n;i++)        is_prime[i]=true;    is_prime[0]=false;    is_prime[1]=false;    for(int i=2;i<=n;i++)    {        if(is_prime[i])        {            prime[p++]=i; //将素数记录到prime中            for(int j=2*i;j<=n;j+=i)  //素数i的倍数的is_prime值全部改为false;                is_prime[j]=false;        }    }    return p;}

上述代码是求从1到n的素数,若是求[a,b)范位内的素数怎么求呢?

原理也是一样,开一个[1,b)的is_prime[ ]数组,按照相同的素数筛处理,最后找在[a,b)范围内的素数即可。


三:快速幂取模

数论问题经常遇见非常大的数,例如 X ^ 22,通常的思路是采用for循环,但当数很大时运算效率会很低。

采用快速幂算法可以将X^22拆为X^16 , X^4和X^2。时间复杂度直降至o(log n)。

typedef long long ll;ll quick_pow(ll x,ll n,ll mod){    ll ans=1;    while(n>0)    {        if(n&1)             ans=ans*x%mod;        x=x*x%mod;        n>>=1;    }    return ans;}


0 0
原创粉丝点击