数论

数论

根号算法

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(xsqrt x))

埃氏筛法

  • 先将2到n范围内的整数列出来,其中2是最小的素数。
  • 将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。
  • 再将表中所有的3的倍数划去……以此类推;
  • 如果表中剩余的最小的数是m,那么m就是素数。
  • 然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数。
2 3 4 5 ... 13 14 15 16 17 18 19 20
2 3 - 5 ... 13 - 15 - 17 - 19 -
2 3 - 5 ... 13 - - - 17 - 19 -
  • 时间复杂度为(O(nlog(logn)))

例:给定一个正整数n(n<=10^6),问n以内有多少个素数?

int prime[MAXN];//第i个素数
bool is_pri[MAXN+10];//is_pri[i]表示i是素数
//返回n以内素数的个数
int sieve(int n)
{
    int p=0;
    for(int i=0;i<=n;i++) is_pri[i]=true;
    is_pri[0]=is_pri[1]=false;
    for(int i=2; i<=n; i++)
    {
        if(is_pri[i])
        {
            prime[++p]=i;
            for(int j=2*i; j<=n; j+=i) is_pri[j]=false;
        }
    }
    return p;
}

区间素数筛:给定两个正整数(a,b(a<bleq 10^{12},b-aleq 10^6)),请问[a,b)内有多少个素数?

#include<iostream>
using namespace std;
bool pri[1000000+10];
bool ispri[10000000+10];//ispri[i-a]=true代表i是素数
void getpri()
{
    memset(pri,true,sizeof(pri));
    pri[0]=pri[1]=0;
    for(int i=2;i<=1000000;i++)
    {
        if(pri[i]){
            for(int j=2*i;j<=1000000;j+=i)pri[j]=0;
        }
    }
}
int main(){
    long long a,b;
    scanf("%lld%lld",&a,&b);
    getpri();
    memset(ispri,true,sizeof(ispri));
    for(long long i=2;i*i<b;i++)
    {
        if(pri[i])
        {
            for(long long j=max((a+i-1)/i,2LL)*i;j<b;j+=i)
                ispri[j-a]=0;
        }
    }
    long long cnt=0;
    for(int i=0;i<b-a;i++)if(ispri[i])cnt++;
    if(a==1)cnt--;
    printf("%lld
",cnt);
}

欧拉

欧拉筛

  • 欧拉筛法的基本思想 :在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。
int prime[maxn];
int visit[maxn];
void Prime()
{
    mem(visit,0);
    mem(prime, 0);
    for (int i = 2;i <= maxn; i++) 
    {
        cout<<" i = "<<i<<endl;
        if (!visit[i]) 
        {
            prime[++prime[0]]=i;//纪录素数,这个prime[0]相当于cnt,用来计数
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) 
        {
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}
  • 上面有一个visit[i*prime[j]] = 1;,这里解释一下
    • 这里不是用i的倍数来消去合数,而是把prime里面纪录的素数,升序来当做要消去合数的最小素因子。
    • i是在消去合数中的作用是当做倍数的。
  • 还有一个i % prime[j] == 0
    • 当 i是prime[j]的倍数时,i=kprime[j],如果继续运算j+1,i * prime[j+1]=prime[j] * kprime[j+1],这里prime[j]是最小的素因子,当i=k * prime[j+1]时会重复,所以才跳出循环。
    • 举个例子 i=8,j=1,prime[j]=2,如果不跳出循环,prime[j+1]=3,83=243=212,在i=12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。
    • 附上欧拉筛代码:
/*求小于等于n的素数的个数*/
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
    int n, cnt = 0;
    int prime[100001];//存素数 
    bool vis[100001];//保证不做素数的倍数 
    scanf("%d", &n);
    memset(vis, false, sizeof(vis));//初始化 
    memset(prime, 0, sizeof(prime));
    for(int i = 2; i <= n; i++)
    {
        if(!vis[i])//不是目前找到的素数的倍数 
        prime[cnt++] = i;//找到素数~ 
        for(int j = 0; j<cnt && i*prime[j]<=n; j++)
        {
            vis[i*prime[j]] = true;//找到的素数的倍数不访问 
            if(i % prime[j] == 0) break;//关键!!!! 
        }
    }
    printf("%d
", cnt);
    return 0;
}

欧拉函数

  • 欧拉函数(φ(x))的值为在[1,x)的区间内与x互质的数的个数
  • 通式:(φ(x)=prod_{i=1}^xn(1−frac{1}{p_i})其中p_1,p_2,...,p_n)为x的所有质因数,x是不为0的整数。(φ(1)=1)
  • 注意:每种质因数只一个。比如(12=2×2×3)那么(φ(12)=12×(1−frac{1}{2})×(1−frac {1}{3})=4)
  • 介绍几个性质:
    • 若n是质数p的k次幂,则(φ(x)=p^k−p^{k−1}=(p−1)p^{k−1}),因为除了p的倍数外,其他数都跟n互质。
    • 积性函数——若m,n互质,(φ(mn)=φ(m)φ(n))
    • 当n为质数时,(φ(2x)=φ(x)),其实与上述类似。
    • 若n为质数则(φ(x)=x−1),这个挺重要的。
    • 一个数的所有质因子之和S是(S=frac {nφ(n)}{2})
//用通式算的
int euler(int n){ //返回euler(n)
    int res=n,a=n;
    for(int i=2;i*i<=a;i++){
        if(a%i==0){
            res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
            while(a%i==0) a/=i;
        }
    }
    if(a>1) res=res/a*(a-1);
    return res;
}
//筛选法打欧拉函数表
#define Max 1000001
int euler[Max];
void Init()
{
     euler[1]=1;
     for(int i=2;i<Max;i++) euler[i]=i;
     for(int i=2;i<Max;i++) if(euler[i]==i)
           for(int j=i;j<Max;j+=i)
                euler[j]=euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100000
int primes[N], euler[N], cnt;
bool st[N];
/* 欧拉函数
可以在 O(n) 的时间复杂度内求出 1~n中所有数的欧拉函数 同时也能算质数
质数存在primes[]中(用cnt统计<=n的素数个数),euler[i] 表示 i的欧拉函数*/
int get_eulers(int n) 
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ ) 
    {
        if (!st[i]) 
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; j < cnt && i * primes[j] <= n; j ++ ) 
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) 
            {
                euler[i * primes[j]] = euler[i] * primes[j];
                break;
            }
            euler[i * primes[j]] = euler[i] * (primes[j] - 1);
        }
    }
    return cnt;
}
 
int main(void) 
{
    get_eulers(101);
    for (int i = 0 ; i < cnt; i++) 
    {
        cout << i << " " << primes[i] << endl;
    }
    cout << endl;
    // 输出1到100每个数的欧拉函数值
    for (int j = 1 ; j < 101; j++) 
    {
        cout << j << " " << euler[j] << endl;
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
原文地址:https://www.cnblogs.com/orange-233/p/12258765.html