简单数论——质数
质数
在 >1 的整数中,如果只包含1和本身两个约数,这个数就被称为质数/素数。
质数的判定——试除法
暴力枚举
bool is_prime(int n)
{
if(n < 2) return ;
for(int i = 2; i < n; i++)
if(n % i == 0) return false;
return true;
}
简单优化
对任意的d能整除n( d|n ), 显然n除上d也能整除n ( | n )
如n = 12,3是12的约数,4也是12的约数; 2是12的约数,6也是12的约数
所有的约数都是成对出现的,在枚举时可以只枚举每一对中较小的那个,即的部分,即
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;
}
分解质因数 ~
hihoCoder 1750
如n = ,可能他的时间复杂度是O(1)的
质因数:在数论里是指能整除给定正整数的质数。每个合数都可以写成几个质数相乘的形式,如6=2*3,8=,这几个质数就叫做这个合数的质因数。
很轻易就可以得出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(n)
当i=2时第一重循环循环了,当n=3时循环了,当n=4时循环了次,以此类推可知
O(n) = + + ... +
= n( + + ... + )
+ + ... + 是一个调和级数,当n->时,这个调和级数 + c
c是一个欧拉常数,约等于0.58
简单优化 埃式筛法
实际上并不需要将所有数的倍数全部删掉,只把质数的倍数删掉就可以了
假设剩下一个p,我们并不需要将2p-1全部枚举一遍来判断p是不是质数,只需要把2p-1的质数判断一遍就可以了
2~p-1的所有质数只要不是p的约数,那么p就是一个质因数
当一个数不是质数我们就不需要筛掉他所有倍数,只有他是质数才会筛掉他的倍数
质数定理:1~n当中有个质数
O(n) = O(n \ ) O(n)
实际复杂度为O(n )
当n=时, = 32, = 5,所以是很小的,与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;
}
}
线性筛法
在以上线性筛法大约比埃式筛法快一倍
核心思路:每一个数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也一定是pji的最小质因子
一共有两种可能:
-
i % primes[j] == 0
- 说明primes[j]一定是i的最小质因子,pj也一定是pj*i的最小质因子
-
i % primes[j] != 0
- 由于我们是从小到大枚举,没有枚举到i的最小质因子
- 说明pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子
不管什么情况,一定有primes[j]是primes[j]*i的最小质因子
任何合数一定会被筛掉,对于任意合数x:
- 假设primes[j]是x的最小质因子,当i枚举到x \ primes[j] 时,x就会被筛掉
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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搭建本