1.判定质数
暴力做法,判断一个数:O(n)
优化暴力做法:只判断一个数的1~根号n O(sqrt(n))
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005;
bool is_prime(int x)
{
//1不是质数,也不是合数
if(x==1) return false;
//从2开始,1不应该加入
for (int i = 2;i <= x / i;i++)
{//i<=n/i 可以防止数据溢出
if (x % i == 0) return false;
}
return true;
}
int main()
{ int q;
cin >> q;
while(q--)
{
int n;
cin>>n;
if (is_prime(n))
{
cout << "Yes"<<'\n';
}
else cout << "No"<<'\n';
}
}
2.分解质因数
基本信息:
概况:算术基本定理:“每一个大于1的整数都能分解成质因数乘积的形式,并且如果把质因数按照由小到大的顺序排列在一起,相同的因数的积写成幂的形式,那么这种分解方法是唯一的。”——又称为“质因数分解定理”
这里有个性质:n中最多只含有一个大于sqrt(n)的因子。证明通过反证法:如果有两个大于sqrt(n)的因子,那么相乘会大于n,矛盾。证毕
于是我们发现最多只有一个大于sqrt(n)的因子,对其进行优化。先考虑比sqrt(n)小的,代码和质数的判定类似
最后如果n还是>1,说明这就是大于sqrt(n)的唯一质因子,输出即可。
证明
1.i为什么一定是质数
首先i是一个最小因子,假设i不是质数,是一个合数,合数必然存在除了1外的其他最小因子,这样子i就不是一个最小因子了,矛盾,证明结束
2.for循环结束,为什么x是一个最大的质数
设for循环结束的x为t
反证法
当t>1时,for循环如果有一个i是大于t的,那么在遇到这个i之前,x会整除t^s,for循环结束后的x就不会是t了,矛盾。证明结束
#include <iostream>
#include <algorithm>
using namespace std;
void divide(int x)
{
for (int i = 2;i <= x / i;i++)
{
if (x % i == 0) //此时i一定是一个质数
{
int s = 0; //表示底数的次方(指数)
while (x % i == 0)
{//i可以被整除,指数+1
x /= i;
s++;
}
cout << i << ' ' << s << ' ' << endl;
//重新循环,x--->x/(i^s)
}
}
//此时还>1就说明他是最大的质因数
//因为小的质因数已经被上一步给整除掉了
if (x > 1)
{
cout << x << ' ' << 1 << endl;
}
cout << endl;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int x;
cin >> x;
divide(x);
}
return 0;
}
3.筛质数
埃氏筛质数法 O(n loglog n)
发现一个质数,就去把 这个质数 作为因子 的所有合数 都标记
void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始
for (int i = 2;i <= n;i++)
{
if (st[i]) //i是合数
continue;
primes[cnt++] = i; //i是质数,加入队列
//这里的j=i*i时间会更快,但是会爆int
for (int j = i + i;j <= n;j += i)
st[j] = true;
}
}
线性筛法(欧拉筛法) O(n)
埃氏筛法的缺陷 :对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。
欧拉筛法:在埃氏筛选法的基础上,只让一个数的最小质因子去染色他,避免重复
两种情况
1.i%primes[j]==0 说明pj是i的最小质因子,同时也是i*pj的最小质因子(染色)
* 由于我们要确保一个合数只会被一个最小质因子染色,所以如果i%pj==0
说明下一个比pj大的质数必然不是最小质因子,没有意义应该break
(下一个pj’*i仍然是以i为约数,可知i能被当前pj染色,下一个pj’不是pj‘*i的最小质因子,pj是)
2.i%primes[j]!=0 说明pj不是i的最小质因子,应该继续往后找到i的最小质因子
但是pj是i*pj的最小质因子,所以也要染色(因为i从2开始,这样子确保了所有以pj为最小质因子的数都会被染色)
问题
1.为什么某个较大质数,例如5,不用让他去染色 5*2,5*3...呢
我们发现,如果一个数t再去染色 t*j (j=0...t-1)的数,那么t必然不是最小质因子,一个数以t为最小质因子的话,t对应的约数一定≥t ,所以只有一个数被放入质数队列中,当前的i>=t,我们才需要去染色 i*t
例如 25=5*5 49=7*7...
还有2^n次方 3^n 都会被 i 遍历到
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 6;
int n;
int cnt; //记录当前有几个质数
bool st[N]; //记录是否是质数
int primes[N]; //记录质数
void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始
for (int i = 2;i <= n;i++)
{
if (!st[i]) //是质数,加入
primes[cnt++] =i;
//这里和埃氏筛法不同的地方在于,即使是一个合数,也会被遍历
//这样子确保了2*2,2*3,2*4...都会被筛选到
for (int j = 0;primes[j] <= n / i;j++) //变形得到primes[j]*i<=n 不去筛选大于n的数
{
st[i * primes[j]] = true;
if (i % primes[j] == 0) //pj是i的最小质因子
break;
}
}
}
int main()
{
cin >> n;
get_primes();
cout << cnt;
}