您的位置:首页 > 其它

基础篇——数论基础

2016-01-20 15:09 197 查看
数论基础主要有三点。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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: