数论 | 质数

文章目录

    • 质数的判定:试除法
    • 分解质因数:试除法
    • 筛质数
      • 朴素做法
      • 优化:埃氏筛法
      • 优化:线性筛法

质数的判定:试除法

不推荐i*i<=n而推荐i<=n/i的原因

可能存在i*i不溢出,但是(i+1)(i+1)溢出的情况

#include 
using namespace std;

int n;

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

int main()
{
    cin >> n;
    for(int i=0; i<n; i++) {
        int a;
        cin >> a;
        if(isPrime(a))  cout << "Yes" << endl;
        else    cout << "No" << endl;
    }
    return 0;
}

时间复杂度: O ( n ) O(\sqrt{n}) O(n )

分解质因数:试除法

  • 分解质因数过程中,最多只含一个大于 n \sqrt{n} n 的质因子
#include 
using namespace std;

void divide(int n)
{
    for(int i=2; i<=n/i; i++) {
        if(n%i == 0) {
            int cnt = 0;
            while(n%i == 0) {
                cnt++;
                n /= i;
            }
            cout << i << " " << cnt << endl;
        }
    }
    if(n > 1)   cout << n << " " << 1 << endl;
    cout << endl;
}

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; i++) {
        int a;
        cin >> a;
        divide(a);
    }
    return 0;
}

时间复杂度:

  • 最坏: O ( n ) O(\sqrt{n}) O(n )
  • 最好【n 是2的倍数】: O ( l g n ) O(lgn) O(lgn)

筛质数

朴素做法

#include 
using namespace std;

const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;

void get_primes(int n)
{
    for(int i=2; i<=n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;  // 将素数存起来
        }
        // 不管是合数还是质数,都用来筛掉后面它的倍数
        for(int j=i; j<=n; j+=i) {
            st[j] = true;
        }
    }
}

int main()
{
    int n;
    cin >> n;
    get_primes(n);
    cout << cnt << endl;
    return 0;
}

时间复杂度:近似 O ( n l g n ) O(nlgn) O(nlgn)

  • T ( n ) = n 2 + n 3 + n 4 + . . . + 1 = n ∗ ( 1 2 + 1 3 + 1 4 + . . . + 1 n ) T(n) = \frac{n}{2}+\frac{n}{3}+\frac{n}{4}+...+1=n*(\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}) T(n)=2n+3n+4n+...+1=n(21+31+41+...+n1)

优化:埃氏筛法

  • 质数定理1~n中有 n l n n \frac{n}{ln{n}} lnnn 个质数
#include 
using namespace std;

const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;

void get_primes(int n)
{
    for(int i=2; i<=n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;  // 将素数存起来
            for(int j=i; j<=n; j+=i) {
                st[j] = true;   // 可以用质数就把所有的合数都筛掉
            }
        }
    }
}

int main()
{
    int n;
    cin >> n;
    get_primes(n);
    cout << cnt << endl;
    return 0;
}

时间复杂度: O ( n l g l g n ) O(nlglgn) O(nlglgn)

优化:线性筛法

  • 每个合数的最小质因子是唯一的。
  • 合数 t 最小因子为 p,最大因数为 i,则有t = p*i,且 p 一定是质数【若 p 不为质数,可继续拆分出最小的质数,剩余部分可以合并到 i 里去】
  • 按照最小质因子筛选,可以保证每个数都只会被筛一遍
  • 虽然primes[]看上去还是那个primes[],但是i再也回不到过去,所以他们结合出来的东西也不会一样。
#include 
using namespace std;

const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;

void get_primes(int n)
{
    for(int i=2; i<=n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;  // 将素数存起来
        }
        for(int j=0; primes[j] <= n/i; j++) {
            // 用最小质因子(对应primes数组)去筛合数
            // 确保会出现2*3但不会重复计算3*2
            st[primes[j]*i] = true;
            // 此时,primes[j]一定是i的最小质因子,无需继续遍历
            // primes要小与等于i的最小质因子
            // 这样能保证每个数遍历一遍,而没有重复
            if(i%primes[j] == 0)    break;
            // 如果这里不break的话,primes[j]已经是最小质因子,primes[j+1] > primes[j],一定不可能是最小质因子
        }
    }
}

int main()
{
    int n;
    cin >> n;
    get_primes(n);
    cout << cnt << endl;
    return 0;
}

你可能感兴趣的:(手撕算法,算法,c++,数据结构,数论,质数)