简单数论——质数

质数

在 >1 的整数中,如果只包含1和本身两个约数,这个数就被称为质数/素数。

质数的判定——试除法

暴力枚举 O(n)
bool is_prime(int n)
{
    if(n < 2) return ;
    for(int i = 2; i < n; i++)
        if(n % i == 0) return false;
    return true;
}
简单优化 O(n)

对任意的d能整除n( d|n ), 显然n除上d也能整除n ( nd | n

如n = 12,3是12的约数,4也是12的约数; 2是12的约数,6也是12的约数

所有的约数都是成对出现的,在枚举时可以只枚举每一对中较小的那个,即dnd的部分,即dn

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

分解质因数 O(logax ~ n)

hihoCoder 1750

如n = 2k,可能他的时间复杂度是O(1)的

质因数:在数论里是指能整除给定正整数的质数。每个合数都可以写成几个质数相乘的形式,如6=2*3,8=23,这几个质数就叫做这个合数的质因数。
很轻易就可以得出n中最多只包含一个大于O(n)的质因子,所以只需要把所有 < O(n) 的质因子枚举出来。

void divide(int x)
{
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0) // 这步成立,i一定是质数
        {
            int s = 0;
            while (x % i == 0) x /= i, s ++ ;
            cout << i << ' ' << s << endl;
        }
    if (x > 1) cout << x << ' ' << 1 << endl;
    cout << endl;
}

筛选质数

hihoCoder 1295


从前往后看,把每一个数的所有倍数全部删掉

第一个数是2,把4,5,8,10,12全部删掉

第二个数是3,把6,9,12全部删掉

第三个数是4,把8,12删掉

如此这么筛选过一遍之后,所有剩下的数就是质数
对于任意一个数x而言,他如果没被删除,就说明p不是2p-1的任意一个数的倍数,即2p-1不存在任何一个p的约数,所以p是一个质数。

朴素做法 O(nlnn)

当i=2时第一重循环循环了n2,当n=3时循环了n3,当n=4时循环了n4次,以此类推可知
O(n) = n2 + n3 + ... + nn

   = n(12 + 13 + ... + 1n)

12 + 13 + ... + 1n是一个调和级数,当n->时,这个调和级数lnn + c

c是一个欧拉常数,约等于0.58


简单优化 埃式筛法

实际上并不需要将所有数的倍数全部删掉,只把质数的倍数删掉就可以了
假设剩下一个p,我们并不需要将2p-1全部枚举一遍来判断p是不是质数,只需要把2p-1的质数判断一遍就可以了
2~p-1的所有质数只要不是p的约数,那么p就是一个质因数
当一个数不是质数我们就不需要筛掉他所有倍数,只有他是质数才会筛掉他的倍数

质数定理:1~n当中有nlnn个质数
O(n) = O(nlnn \ lnn) O(n)
实际复杂度为O(n loga logax)
当n=232时,log232 = 32, log2log232 = 5,所以loga logax是很小的,与O(n)是一个级别的

int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (st[i]) continue;
        primes[cnt ++ ] = i;
        for (int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}
线性筛法

107以上线性筛法大约比埃式筛法快一倍

核心思路:每一个数n只会被他的最小质因子筛掉
每一个数只有一个最小质因子,即每个数只会被筛一次,所以他是线性的

int primes[N], cnt;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

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 ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break; // 这句话执行证明primes[j]一定是i的最小质因子,因为j是从小到大进行的枚举
        }
    }
}

j是从小到大枚举的所有质数,每一次把当前质数和i的乘积筛掉

i%pj == 0
说明primes[j]一定是i的最小质因子,pj也一定是pji的最小质因子
i%pj != 0
由于我们是从小到大枚举,没有枚举到i的最小质因子
说明pj一定小于i的所有质因子,pj也一定是pj
i的最小质因子

一共有两种可能:

  1. i % primes[j] == 0

    • 说明primes[j]一定是i的最小质因子,pj也一定是pj*i的最小质因子
  2. i % primes[j] != 0

    • 由于我们是从小到大枚举,没有枚举到i的最小质因子
    • 说明pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子

不管什么情况,一定有primes[j]是primes[j]*i的最小质因子

任何合数一定会被筛掉,对于任意合数x:

  • 假设primes[j]是x的最小质因子,当i枚举到x \ primes[j] 时,x就会被筛掉
posted @   晓尘  阅读(421)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示