欧拉筛/线性筛素数(数论)
模板题
代码
#include<iostream>
#include<algorithm>
using namespace std;
int a[100000008] = { 0 };
int pri[100000008];
int n, q;
int num = 0;
void check(int n)
{
for (int i = 2; i <= n; i++)
{
if (a[i] == 0)
{
pri[++num] = i;
}
for (int j = 1; j <= num; j++)
{
if (i * pri[j] > n)
{
break;
}
a[i * pri[j]] = 1;
if (i % pri[j] == 0)
{
break;
}
}
}
}
int main()
{
cin >> n >> q;
check(n);
sort(pri, pri + num);
while (q--)
{
int x;
cin >> x;
cout << pri[x] << endl;
}
return 0;
}
最小质因数是指一个合数能分解出的最小的质数因数。以下是详细介绍:
- 定义:每个合数都可以写成几个质数相乘的形式,其中最小的那个质数就是这个合数的最小质因数。例如,对于合数12,它可以分解为12 = 2×2×3,其中2是最小的质数因数,所以12的最小质因数是2。
- 特殊情况:质数的最小质因数就是它本身。因为质数只有1和它自身两个因数,所以它的最小质因数就是这个质数本身。例如,7是质数,它的最小质因数就是7。
- 求法:可以从最小的质数2开始,依次用质数去试除给定的数,第一个能整除该数的质数就是它的最小质因数。例如,求30的最小质因数,先尝试2,30\2 = 15,所以30的最小质因数是2。
分享
算法原理
欧拉筛法的基本思想是每个合数只被它的最小质因子筛掉一次,从而避免了重复筛选。算法通过两个数组实现,第一个数组筛选,第二个数组保存质。
输入数据范围n,在 main
函数中通过循环遍历每一个元素 i
。检查 a
数组的对应位置 a[i]
是否为 0,如果为 0,则证明 i
为素数,将其记录到 pri
数组中,并更新记录质数的数量 num
。
然后进入 for (int j = 1; j <= num; j++)
循环,每次遍历每一个记录下来的素数 pri[j]
,将当前的 i
的值与每一个记录的素数相乘,并将乘积 i * pri[j]
对应的 a
数组位置标记为 1,代表该数已经确定为合数。
特别地,当 i % pri[j] == 0
时,要提前跳出内层循环。这是因为此时 pri[j]
是 i
的最小质因数,若继续循环,后续得到的乘积 i * pri[j + k]
(k > 0
) 这个合数本应被 pri[j]
筛去(因为 pri[j]
是其最小质因数),却会被 pri[j + k]
提前筛去,导致重复标记。通过这个判断保证了每个合数只会被它的最小质因数筛去,实现了线性筛法的高效性。
if (i % pri[j] == 0) { break; }
这行代码是线性筛法(欧拉筛法)的核心优化部分,它的作用是确保每个合数都只会被它的最小质因数筛去,避免重复标记