筛质数 + 质因子分解

试除法判断质数

最常用的方法就是用试除法,复杂度O(sqrt(N))
首先有下列几种写法:

  • 一、最暴力的写法,比较慢
bool is_prime(int n)
{
    if(n < 2) return false;
    for(int i = 2; i < n; i++)
        if(n % i == 0)
            return false;
    return true;    
}
  • 二、优化写法,但是由于每次执行sqrt(n),效率太低了
bool is_prime(int n)
{
    if(n < 2) return false;
    for(int i = 2; i < sqrt(n); i++)
        if(n % i == 0)
            return false;
    return true;     
}
  • 三、ii可能会溢出 不好
bool is_prime(int n)
{
    if(n < 2) return false;
    for(int i = 2; i * i < n; i++)
        if(n % i == 0)
            return false;
    return true;     
}
  • 最好的方法:因为因数都是成对出现 因此可以直接枚举到n/i即可,并且这样一定不会溢出
bool is_prime(int n)
{
    if(n < 2) return false;
    for(int i = 2; i <= n / i; i++) //这样写一定不会溢出
        if(n % i == 0)
            return false;
    return true;     
}

质数筛:

埃拉托斯特尼筛法 O(loglogn):

  • 1.从前往后,把每一个数的倍数筛掉:
void get_primes1(int n) {
    for (int i = 2; i <= n; i++) {
        if(!st[i]) { //如果是质数
            primes[cnt++] = i;
        }
        for (int j = i + i; j <= n; j += i) st[j] = true; //把每一个数的倍数筛掉
    }
}
  • 2.再优化:把质数的倍数筛掉:
void get_primes2(int n) {
    for (int i = 2; i <= n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;
            for (int j = i + i; j <= n; j += i) st[j] = true; //把每一个质数的倍数筛掉
        }
    }
}

代码:

using namespace std;

const int N = 1e6 + 10;
int primes[N], cnt;
bool st[N];

void get_primes1(int n) {
    for (int i = 2; i <= n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;
        }
        for (int j = i + i; j <= n; j += i) st[j] = true; //把每一个数的倍数筛掉
    }
}

//再优化:把每一个质数的倍数筛掉即可,因此把循环放if里边即可
void get_primes2(int n) {
    for (int i = 2; i <= n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;
            for (int j = i + i; j <= n; j += i) st[j] = true; //把每一个质数的倍数筛掉
        }
    }
}

int main() {
    int n;
    cin >> n;
    get_primes1(n);
    cnt = 0;
    get_primes2(n);
    cout << cnt << endl;
    system("pause");
    return 0;
}

但是这种方法有种缺陷,有些数会被重复筛选,例如26会被2筛掉,也会被13筛掉,因此为了改变这种缺陷我们可以用一种方法就是保证被筛掉的每一个数都是被它的最小质因子晒掉,那么复杂度就成了O(n),这就是欧拉筛法

欧拉筛选:

详细解释就看注释吧

#include <iostream>

using namespace std;

const int N = 100;
int primes[N], cnt;
bool st[N];

void get_primes1(int n) {
    for (int i = 2; i <= n; i++) {
        if(!st[i]) {
            primes[cnt++] = i;
        }    
        //枚举到n / i是因为:这里的primes[j]是最小质因子,i是某一个合数,i*primes[j]一定是满足<=x的,而且这样写可以防止越界
        for (int j = 0; primes[j] <= n / i; j++) { //从小到大枚举所有质数
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break; //代表primes[j]一定是i的最小质因子,
                                            //因此primes[j]一定是primes[j]*i的最小质因子,这里直接break也是为了避免重复筛选   
        }
    }
}   
//对于st[primes[j] * i] = true的成立性解释:
//如果i % primes[j] == 0
//代表primes[j]一定是i的最小质因子,因此primes[j]也一定是primes[j]*i的最小质因子,这样可以保证每个数只被晒一次(个人理解)

//如果i % primes[j] != 0   primes[j]一定小于i的所有质因子 primes[j]也一定是primes[j]*i的最小质因子
//因此筛的每一个数一定是用最小质因子来筛选掉 

int main() {
    int n;
    cin >> n;
    get_primes1(n);
    cout << cnt << endl;
    system("pause");
    return 0;
} 

最后补充一下质因子分解,这个知识点:

给定n个正整数ai,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。

输入格式
第一行包含整数n

接下来n行,每行包含一个正整数ai

输出格式
对于每个正整数ai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。

每个正整数的质因数全部输出完毕后,输出一个空行。

数据范围
1n100
1ai2109
输入样例:

2
6
8

输出样例:

2 1
3 1

2 3

首先看代码:

#include <iostream>

using namespace std;

void divide(int x) {
    for (int i = 2; i <= x; i++) {
        if(x % i == 0) {
            int s = 0;
            while(x % i == 0) { //求次数
                x /= i;
                s++;
            }
            cout << i << " " << s << endl;
        }
    }
}

int main() {
    int n;
    cin >> n;
    while(n--) {
        int x;
        cin >> x;
        divide(x);
    }
    system("pause");
    return 0;
}

这样写,第一眼可能觉得我们为什么不是从小到大枚举所有的质因数而是枚举所有数,这样做会不会被合数分解,答案是不会的:

这里不必专门从小到大枚举所有的质因数因为根据算术基本定理:任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成
有限个质数的乘积N=P1a1P2a2P3a3......Pnan

例如16 就会被分解成2222 因此根本不会枚举到合数
因此当我们枚举到i的时候 x里就不包含有任何从(2(n1))的质因子了 因此只要x成立那么i就一定是质数.

再优化:

  • 一个很重要的性质:x中只包含一个大于sqrt(n)的质因数 利用反证法证明:如果有两个大于sqrt(n)的质因数那么相乘就大于n了,
    就违背了算数基本定理,因此只需枚举到x/i即可,最后还要判断一下是否那唯一一个大于sqrt(n)的质因数。

代码:

#include <iostream>

using namespace std;

void divide(int x) {
    for (int i = 2; i <= x / i; i++) {
        if(x % i == 0) {
            int s = 0;
            while(x % i == 0) { //求次数
                x /= i;
                s++;
            }
            cout << i << " " << s << endl;
        }
    }
    if(x > 1) cout << x << " " << 1 << endl;
    puts("");
}

int main() {
    int n;
    cin >> n;
    while(n--) {
        int x;
        cin >> x;
        divide(x);
    }
    system("pause");
    return 0;
} 
posted @   Xxaj5  阅读(197)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示