质数有关知识总结加复习

一、基本的思想和定理。

算数基本定理,也叫唯一分解定理。指的是一个大于一的合数都可以分解为有限个质数相乘。即N=p1a1p2a2*.....*pnan。p即为质数,a为质数的指数。

二、一些重要的公式。

1、求约数个数和约数的和。

 给定一正整数n,求n的所有约数的个数。公式:num=(a1+1)*(a2+1)*......*(an+1)。至于原理,我理解的思路是,按照组合原则,p1a1一共有(a1+1)种情况,其余的以此类推。

2、求约数个数的和。

给定一个正整数n,求n的所有约数之和。公式:sum=(1+p11+p12+.....+p1n)*(1+p21+p22+.....+p2n)*......*(1+pn1+pn2+...+pnn)。原理就是,把n的约数和分解为pa的约数和。

3、欧拉函数。

φ(n)表示为某一个正整数的欧拉函数:小于n的正整数中和n互质的正整数的个数。

欧拉函数具有以下三个特性:

// 1、φ(n)=n*(1-1/p1)*(1-1/p)*(1-1/p3)*.....*(1-1/pn);

// 2、若m为质数,n%m==0,则φ(m*n)=m*φ(n);

// 3、若m,n互质,则φ(n*m)=φ(m)*φ(n)。

三、C++代码。

1、如何判断素数。

// 常规的方法是判断n是否能被2到n之间的数整除,如果能够整除,则说明n有除了1以外的其他因数,则n不是质数。 

优化后的思路是:假如存在一个数(不一定为正整数)i,使得i*i=n,则我们可以从2到i之间的数进行判断,因为两个数之间成反比关系,一个肯定大于等于i,另一个肯定小于等于i。那么,只要保证在2到i之间没有正整数可以整除n,我们就可以知道,n一定为质数。

bool is_prime(int n)
{
    for (int i = 2; i * i <= n;i++){
        if(n%i==0){
            return false;
        }
    }
    return true;
}

2、如何分解质因数。

// 分解质因数首先从最小的质数开始分解,如果能够被整除,则一直循环到不能被整除为止,在此期间还可以计算质因数出现的次数。

同样也可以进行优化,和如何判断质数的优化方法一样,如果有这样的数i,我们就可以在<=i处提前判断完成,因为在判断前面的质数之后,大于i的部分不可能会有质数再被整除。

除非当我们整除这些质数之后的n,本身就是一个比较大的质数,这时候,我们可以在最后判断n是否大于1,是的话,n即为质数。比如1004=2^2*251。在我们枚举2之后,n的值就变成了251,这时候从3到sqrt(251,1/2)之间都没有数整除n。

vector<pair<int,int>> v;
int Prime_factorization(int n)
{
    for (int i = 2; i * i < n;i++){
        if(n%i==0){
            int s = 0;
            while(n%i==0){
                n /= i;
                s++;
                v.push_back({i, s});
            }
        }
    }
    if(n>1){
        v.push_back({n, 1});
    }
}

// 如果对动态数组不熟悉,可以尝试用结构来构建。

3、筛选素数之埃式筛选法(E_sieve)

// 埃式筛选的原理也很简单,在我们找到一个素数之后,素数的倍数一定不是素数。根据这一原则,我们就可以进行筛选了。

优化的地方和上面一样,首先是找素数的过程,到i处就可以了。然后是在素数的倍数方面,当i=5时,2*5,3*5,4*5已经在前面优化过了。这时候从5*5开始筛选就可以了。

时间复杂度O(nloglogn)

int primes[maxx];
bool vis[maxx];
void E_sieve(int n)
{
    memset(vis, 0, sizeof(vis));
    int k = 0;
    for (int i = 2; i * i <= n;i++){
        if(!vis[i]){
            primes[k++] = i;
            for (int j = i * i; j <= n;j += i){
                vis[primes[j] * i] = true;
            }
        }
    }
}

4、筛选素数之线性筛(Linear_sieve)

// 线性筛主要是针对埃筛的一些问题进行解决,以使得时间复杂度降低。埃筛的主要问题是一个数可以被两个质数筛选。即使是优化后的代码。比如12被2和3两个质数筛选。

优化方面会有一些难懂。推荐先记忆模板,再慢慢理解。

优化的地方在:我们在筛选的时候,让合数被最小质因数筛选。加一个判定条件就可以了。

时间复杂度O(n)

int primes[maxx];
bool vis[maxx];
void Linear_sieve(int n)
{
    int k = 0;
    for (int i = 2; i * i <= n;i++){
        if(!vis[maxx]){
            primes[k++] = i;
        }
        for (int j = 0; primes[j] * i <= n;j++){
            vis[primes[j] * i] = true;
            if(i%primes[j]==0){
                break;
            }
        }
    }
}

对于第二个for循环里的if语句,就是说,如果i可以整除primes[j],那么primes[j+1]一定不是primes[j+1】*i的最小质因数。比如i=2,primes[0]=2;2%2=0,下一个质数为3,而2*3的最小质因数为2,因此break;

// 可以自己手动列式子理解一下。

5、筛选素数之欧拉筛。//放到后面去讲

6、求所有约数

// 和找质因数一样的道理。

vector<int> v;
void Divisor_find(int n)
{
    for (int i = 1; i * i <= n;i++){
        if(n%i==0){
            v.push_back(i);
            if(i!=n/i){
                v.push_back(n / i);
            }
        }
    }
}

7、求所有约数的个数。

// 根据公式求就可以了。分解质因数记录质数的指数。

int cnt[maxx];
int k = 0;
int Number_of_divisor(int n)
{
    for (int i = 2; i * i <= n;i++){
        if(n%i==0){
            int s = 0;
            while(n%i==0){
                n /= i;
                s++;
            }
            cnt[k++] = s;
        }
    }
    int sum = 0;
    for (int i = 0; i < k;i++){
        sum += (cnt[i] + 1);
    }
    return sum;
}

8、求所有约数的和。

// 一样的道理,根据公式求即可。不过要注意的是,可能会用到费马小定理。但是这里我们还是要注意一个公式就是x/y%mod可以转换为x%(y*mod)/y;

ll quickpow(ll a,ll b)
{
    ll res = 1;
    while(b){
        if(b&1){
            res = (res * a);
        }
        b >>= 1;
        a = (a * a);
    }
    return res;
}

int a[maxx], b[maxx];
int k = 0;
int Sum_of_divisor(int n)
{
    for (int i = 2; i * i <= n;i++){
        if(n%i==0){
            int s = 0;
            while(n%i==0){
                n /= i;
                s++;
            }
            a[k] = i;
            b[k] = s;
            k++;
        }
    }
    if(n>1){
        a[k] = n;
        b[k] = 1;
        k++;
    }
    int sum;
    for (int i = 0; i < k;i++){
        sum += (quickpow(a[i], b[i] + 1) - 1) / (1 - a[i]);
    }

    return sum;
}

9、欧拉函数

// n*(i-1/p)可以换成n/p*(p-1)。然后按公式写就可以了。

int Euler_function(int n)
{
    int res = n;
    for (int i = 2; i * i <= n;i++){
        if(n%i==0){
            res = res / i * (i - 1);
            while(n%i==0){
                n /= i;
            }
        }
    }
    if(n>1){
        res = res / n * (n - 1);
    }
    return res;
}

10、欧拉筛

// 根据欧拉函数三个特性写

int primes[maxx], phi[maxx];
bool vis[maxx];
void Euler_sieve()
{
    memset(vis, 0, sizeof(vis));
    int cnt = 0;
    for (int i = 2; i * i <= n;i++){
        if(!vis[i]){
            primes[cnt++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; primes[j] * i <= n;j++){
            vis[primes[j] * i] = true;
            if(i%primes[j]==0){
                phi[primes[j] * i] = primes[j] * phi[i];
                break;
            }
            else{
                phi[primes[j] * i] = phi[i] * (primes[j] - 1);
            }
        }
    }
}
原文地址:https://www.cnblogs.com/Drqidian/p/13398484.html