数论知识点2——素数筛法
1.素数筛法
在判断素数的时候,如果是小范围的,查询次数比较少的判断,一般是使用循环到sqrt(n)之前,判断是否能够整除,但是如果数据量很大一般用miller_rabbin算法,这里想说的是,数据量不大,十万到百万左右,但是查询次数比较多的情况,这时候一般使用埃式筛法,先打一张表之后就随便使用了,每次查询都是O(1),而筛法虽然是双重for循环但是实际上时间复杂度可以认为是线性的,即O(N)。
算法思想如下:筛法主要利用了倍数关系,我们知道2是素数,但是所有2的倍数都不是素数,比如4,6, 8, 10,因为他们至少有2这个因子,同理3,5,7,11也是这样判断的。
代码如下:
const int maxn = 10000;
bool isprime[maxn];
void init()
{
memset(isprime, true, sizeof(isprime));
isprime[1] = false;
for(int i = 2; i < maxn; ++i)
if(isprime[i])
for(int j = i * 2; j < maxn; j +=i)
isprime[j] = false;
}
isprime数组表示是否为素数,true表示是素数, 一开始初始化全为true,从2开始,将所有2的倍数标记为false,很明显之后循环的时候由于有if(isprime[i]) ,所有像4,6,8,这些值根本不会带入第二个for循环,这就是为什么筛法无限接近O(N)的原因了,当2,3,5筛一轮以后,其实剩下的没有标记的数,已经特别少了,这个算法运行的越来越快,以至于到后期第二个for循环基本不执行了。
2.给一道需要用到筛法的题目HDOJ 2136
这道题目的意思就是询问一下某个数的最大素因子在素数表中的位置,特别的1的位置是0,也就是我们的素数表是1,2,3,5,7,对于题目中给的样例,4的最大素因子是2,在表中的位置是1,所以输出1,其他的素数最大素因子就是本身,所以5输出3, 7输出4。
这个题目的解法实际上可以利用刚刚我们打素数表的时候,在对2的倍数进行标记的时候,很明显这些4,6,8,10他们的最大素因子(暂时)是2,在对5的倍数进行标记的时候,10的最大素因子更新为5,所以最后的代码可以这么写:
const int maxn = 1000005;
bool isprime[maxn];
int sum[maxn];
void init()
{
memset(isprime, true, sizeof(isprime));
isprime[1] = false;
sum[1] = 0;
int k = 1;
for(int i = 2; i < maxn; ++i)
if(isprime[i])
{
for(int j = i * 2; j < maxn; j += i)
{
isprime[j] = false;
sum[j] = k;
}
sum[i] = k++;
}
}
利用sum记录位置,我们知道k之所以自增,是因为2这个素数用完了,下一次使用的是3,所以要自增。另外这道题目一定要使用scanf读入,用cin超时了。。。