基础篇——数论基础

数论基础主要有三点。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;
}


  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值