要求1-100000范围内的素数,如果用自定义函数挨个求,对于大范围的求素数会非常耗时。复杂度为O(n * sqrt(n)),所以可以用素数筛法来求大范围内的素数
说一下原理:
开一个标记数组,全部初始化为true,0、1不是素数,直接从数组里划掉。
从2开始,凡是2的倍数、且小于100000的,全部标记为false。
再找2以后的、是素数的下一位数,是3
从3开始,凡是3的倍数、且小于100000的,全部标记为false。
再找3以后的、是素数的下一位数,是5
从5开始,凡是5的倍数、且小于100000的,全部标记为false。
这样循环,直到开始的位置大于100000,退出循环。
这样,所有标记为TRUE的元素的下标全是素数。
贴上代码
bool isprime[1000010];
memset(isprime, true, sizeof(isprime));
for (int i = 2; i <= 1000; i++)
{
if (isprime[i])
for (long long j = 2; j * i <= 1000; j++)
isprime[j * i] = false;
}
- 然而这样做会有重复的标记为 false 的过程,想要提高效率,还可以采用下面这种办法,在对大数据处理的时候会有明显的优势
bool isprime[1010];
memset(isprime, true, sizeof(isprime));
for (int i = 2; i <= 1000; i++)
{
if (isprime[i])
for (long long j = 2; j * i <= 1000; j++)
isprime[j * i] = false;
}
解释一下 for (i = j * j; i <= 1000000; i += j + j) 这个循环
首先从 i + = j + j 说起:
首先在上面已经把所有的偶数位全部标记为 false,所以,当 a[j] 能进入在这个 for 循环上面if的时候,j 一定是个奇数。i 的起始值是 j * j 所以,起始值也一定是个奇数。
所以i一下加两个 j ,因为加上一个j后一定是个偶数。
在说起始值为什么为 j * j :
像上面所说,j是个奇数。
对于 j * (j - 1) ,因为j - 1是个偶数,所以 j * (j - 1) 一定是个偶数,其所在位置一定标为false 。
对于j * (j - 2k) (k = 1, 2, 3…)j - 2k 一定是个奇数,对于任意的j * (j - 2k),一定有这样一个循环标记过j * (j - 2k):for(i=(j-2k) * (j - 2k); i <= 1000000; i += j + j),所以j * (j - 2k)一定是一个确定了的位置
PS:注意第二层循环要用 long long ,因为 i * i 可能会产生一个大于 int 类型的数,这时候用 int 类型变量接收会产生段错误。