数论-质数
质数
1.定义:
在大于1的整数中,只包含1和本身这两个约数的数称为质数或者叫素数.而合数是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数(1既不是质数也不是合数,质数和合数是针对所有大于1的 “自然数” 来定义的)
2.质数判定
(1)试除法:
思路:
一个合数的约数总是成对出现的,如果d|n
,那么(n/d)|n
,因此我们判断一个数是否为质数的时候,只需要判断较小的那一个数能否整除n就行了,即只需枚举d<=(n/d)
,即d*d<=n
,d<=sqrt(n)
就行了 BUT sqrt(n)
这个函数执行的时候比较慢,而且第一个容易爆int所以首选第一个
时间复杂度:
o(sqrt(n))
代码:
#include <iostream>
using namespace std;
int n,a;
bool is_isprime(int x)
{
if(x<2) return false;//不符合情况直接返回false
for(int i=2;i<=x/i;i++)//每一个数的约数都是成队出现的 所以可以从2枚举到较小的那个数,避免了数据溢出和使用函数的情况
if(x%i==0) return false;
return true;//是素数
}
int main()
{
cin>>n;
while(n--)
{
cin>>a;
if(is_isprime(a)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
3.分解质因数
思路:
原理:唯一分解定理(算数基本定理))
注意-----分解质因数与质因数不一样!!分解质因数是一个过程,而质因数是一个数.
一个合数分解而成的质因数最多只包含一个大于sqrt(n)
的质因数(反证法,若n可以被分解成两个大于sqrt(n)
的质因数,则这两个质因数相乘的结果大于n,与事实矛盾)当枚举到某一个数i的时候,n的因子里面已经不包含2-i-1
里面的数,如果n%i==0,则i的因子里面也已经不包含2-i-1
里面的数,因此每次枚举的数都是质数.
- 算数基本定理(唯一分解定理):任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积N=P1a1P2a2P3a3......Pnan,这里P1<P2<P3......<Pn均为质数,其中指数ai是正整数。这样的分解称为 N 的标准分解式。最早证明是由欧几里得给出的,由陈述证明。此定理可推广至更一般的交换代数和代数数论。
- 质因子(或质因数)在数论里是指能整除给定正整数的质数。根据算术基本定理,不考虑排列顺序的情况下,每个正整数都能够以唯一的方式表示成它的质因数的乘积。
- 两个没有共同质因子的正整数称为互质。因为1没有质因子,1与任何正整数(包括1本身)都是互质。
- 只有一个质因子的正整数为质数。
时间复杂度:
o(log(n)~sqrt(n))具体情况具体分析
代码:
#include <iostream>
using namespace std;
int n,a;
void divide(int n)
{
for(int i=2;i<=n/i;i++)
if(n%i==0)//i一定是质数
{
int s=0;
while(n%i==0)
{
n/=i;
s++;
}
cout<<i<<' '<<s<<endl;
}
if(n>1) cout<<n<<' '<<1<<endl;//特殊处理
puts("");
}
int main()
{
cin>>n;
while(n--)
{
cin>>a;
divide(a);
}
return 0;
}
4.筛质数
(1)埃氏筛
思路:
质数定理:1~n中有n/lnn个质数
原理:在朴素筛法的过程中只用质数项去筛.
时间复杂度:粗略估计:O(n).实际:O(nlog(logn)).
1~n中,只计算质数项的话,”1/2+1/3+1/4+1/5+…+1/n”的大小约为log(logn).
代码:
//埃氏筛法 o(nloglogn)
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,cnt;
bool st[N];//筛子
int primes[N];//存质数
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=n;//保存质数
for(int j=i+i;j<=n;j+=i) st[j]=true;//将质数的倍数筛掉
}
}
}
int main()
{
cin>>n;
get_primes(n);
cout<<cnt;
return 0;
}
(2)线性筛法:
**思路:
核心:1~n内的合数p只会被其最小质因子筛掉.
原理:1~n之内的任何一个合数一定会被筛掉,而且筛的时候只用最小质因子来筛,
然后每一个数都只有一个最小质因子,因此每个数都只会被筛一次,因此线性筛法是线性的.
枚举到i的最小质因子的时候就会停下来,即”if(i%primes[j]==0) break
;”.
因为从小到大枚举的所有质数,所以当”i%primes[j]!=0
”时,primes[j]
一定小于i的最小质因子,
primes[j]
一定是primes[j]i
的最小质因子.
因为是从小到大枚举的所有质数,所以当”i%primes[j]==0
”时,primes[j]
一定是i的最小质因子,
而primes[j]
又是primes[j]
的最小质因子,因此primes[j]
是iprimes[j]
的最小质因子.
- 关于for循环的解释:
注:首先要把握住一个重点:我们枚举的时候是从小到大枚举的所有质数
1.当i%primes[j]==0
时,因为是从小到大枚举的所有质数,所以primes[j]
就是i的最小质因子,而primes[j]
又是其本身primes[j]
的最小质因子,因此当i%primes[j]==0
时,primes[j]
是primes[j]i
的最小质因子.
2.当i%primes[j]!=0
时,因为是从小到大枚举的所有质数,且此时并没有出现过有质数满足i%primes[j]==0
,因此此时的primes[j]
一定小于i的最小质因子,而primes[j]
又是其本身primes[j]
的最小质因子,所以当i%primes[j]!=0
时,primes[j]
也是primes[j]i
的最小质因子.
3.综合1,2得知,在内层for循环里面无论何时,primes[j]
都是primes[j]i
的最小质因子,因此”st[primes[j]i]=true
”语句就是用primes[j]i
这个数的最小质因子来筛掉这个数.
时间复杂度:
若n在10的6次方的话,线性筛和埃氏筛的时间效率差不多,若n在10的7次方的话,线性筛会比埃氏筛快了大概一倍.
代码:
#include <iostream>
using namespace std;
const int N=1e6+10;
bool st[N];
int primes[N];
int n,cnt;
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]一定是最小质因子
}
}
}
int main()
{
cin>>n;
get_primes(n);
cout<<cnt;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析