码迷,mamicode.com
首页 > 其他好文 > 详细

数论相关知识

时间:2020-02-04 10:40:18      阅读:101      评论:0      收藏:0      [点我收藏+]

标签:质数   mat   范围   正文   区间   can   art   png   htm   

目录

0 前导知识

快速幂

大数乘积取模

1 质数

根号算法

埃氏筛

欧拉筛

米勒罗宾素数检测

2 公因数与质因数

质因数分解

公因数

3 欧拉函数

欧拉函数

欧拉降幂

费马小定理

以下正文

前导知识:快速幂,大数乘积取模

快速幂

有二进制非递归和基本递归两种做法,代码呈现的是二进制非递归

#include <cstdio>
#include <cmath>
using namespace std;
int main(){
    long long ans=1,i,j,k,m,n,b,p;
    scanf("%lld%lld%lld",&b,&m,&p);
    printf("%lld^%lld mod %lld=",b,m,p);
    while(m>0){
        if(m%2==1)
            ans=ans*b%p;
        b=b*b%p;
        m=m>>1;
    }
    printf("%lld",ans%p);
    return 0;
}

大数乘积取模

计算\((a\times b)\%p\)怎么办?\(((a%p)\times (b%p))\%p\)还是会溢出。
下面用到一种思想,神奇与上面的快速幂有异曲同工之妙,把\(b\)看成二进制表示。
举个栗子:\(4\times 13\%p\),看成是\(4\times 1101(2)\%p\),其实表示的是\(4\times (1\times 2^3+1\times 2^2+0\times 2^1+1\times 2^0)\%p\),那么我们在计算的时候就把b看成二进制,如果二进制最后一位是1,就说明这一位应该乘\(a\)取余,为零说明这一位不用乘\(a\),从低位开始不断将\(b\)的二进制式右移,同时将\(a\)乘以2,等同于把基数平方,原因见上式。

#include <iostream>
using namespace std;
long long q_mod(long long a,long long n,long long p)
{
    a = a%p;
    //首先降a的规模
    long long sum = 1;//记录结果
    while(n)
    {
        if(n&1)
        {
            sum = (sum*a)%p;//n为奇数时单独拿出来乘
        }
        a = (a*a)%p;//合并a降n的规模
        n /= 2;
    }
    return sum;
}
long long q_mul(long long a,long long b,long long p)
{
    long long sum = 0;
    while(b)
    {
        if(b&1)//如果b的二进制末尾是零
        {
            (sum += a)%=p;//a要加上取余
        }
        (a <<= 1)%=p;//不断把a乘2相当于提高位数
        b >>= 1;//把b右移
    }
    return sum;
}

可以发现两者非常的相似,差别在于结果变量的初值和计算中加号和乘号的区别。
参考文章:
快速幂取模(详细),https://blog.csdn.net/dbc_121/article/details/77646508
大数乘法取模运算(二进制),https://www.cnblogs.com/geloutingyu/p/5886626.html

1 质数

根号算法

bool isprime(int x){
    if(x==0||x==1) return 0;
    if(x==2) return 1;
    for(int i=3;i<=sqrt(x);i++){
        if(x%i==0) return 0;
    } 
    return 1;
} 

单次时间复杂度:\(O(\sqrt{x})\)
判断1-n的素数个数:\(O(x\sqrt{x})\)

埃氏筛

技术图片
时间复杂度:\(O(n\log(\log n))\)

埃氏筛的几个应用

1

题意描述

\(N\)组测试数据,求区间\([a,b]\)上的所有质数。

数据范围

\(N\leq 10^4,a,b\leq 10^6\)

#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//总的范围规定在这里
using namespace std;
 
//我们将这个埃氏筛法写成一个函数
bool isprime[maxn];
void sieve(){
    for(int i=0;i<=maxn;i++)isprime[i]=true;
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=maxn;i++){//从2开始往后筛
        if(isprime[i]){
            for(int j=2*i;j<=maxn;j+=i){
                isprime[j]=false;
            }
        }
    }
}
 
int l,r; 
int main(){
    //我们在程序刚开始 先调用这个函数
    //把这个isprime数组处理成我们想要的样子 用来判断素数
    //这就是预处理的思想 我们在开头处理这一次
    //把isprime数组 里面 下标是素数的全部变成了true
    //后边想判断是不是素数 直接用isprime[i]是不是真就好了
    sieve();
    
    
    int cnt=0;//计数
    scanf("%d%d",&l&r);//输入 l和r
    for(int i=l;i<=r;i++){//遍历 l到r  判断就行了
        if(isprime[i]){
            cnt++;
        }
    }
    printf("%d",cnt);
}
2

题意描述

输入一个数\(n\),判断他是不是素数,\(N\)组测试数据

数据范围

\(n\leq 10^6\)

题解

这个时候 就体现了 预处理的重要性
我们先预处理出来 1e6以内的所有素数? 这样不管你输入啥? 我直接去看 是不是素数就好了
预处理 按照一般的算法 20以内的素数需要60次才能判断出来
那1e6以内? 大概需要1e9次(也就是十亿次)
而用埃氏筛法需要70万次
这就是这个算法的效率?

#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//总的范围规定在这里
using namespace std;
 
//我们将这个埃氏筛法写成一个函数
bool isprime[maxn];
void sieve(){
    for(int i=0;i<=maxn;i++)isprime[i]=true;
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=maxn;i++){//从2开始往后筛
        if(isprime[i]){
            for(int j=2*i;j<=maxn;j+=i){
                isprime[j]=false;
            }
        }
    }
}
 
int n; 
int main(){
    sieve();//预处理
    
    //输入 n
    while(scanf("%d",&n)!=EOF){
        if(isprime[n]){
            printf("Yes\n");
        }
        else{
            printf("No\n");
        }
    }
}

欧拉筛

米勒罗宾素数检测

2 公因数与质因数

质因数分解

公因数

3 欧拉函数

欧拉函数

欧拉降幂

费马小定理

数论相关知识

标签:质数   mat   范围   正文   区间   can   art   png   htm   

原文地址:https://www.cnblogs.com/liuziwen0224/p/shulun1.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!