线性筛法(欧拉筛法)

昨天和学长(没错就是这位神秘的学长)探讨了一道判断素数的题目,今天把这道题目的总结和心得写一下。
首先,特别要感谢提供资料的(和蔼可亲的)Tan Sir.

(记得和同学探讨快速幂实现问题的时候,我写了个O(n)的判断素数,当时顺便提了一下有更简单的算法,刚好遇到类似的题目拿出来写一下)
先介绍一下普通的筛法求素数。

普通筛法

原理:素数的倍数一定不是素数。
我们可以用一个长度为n+1的数组来存储信息(这种用另一个数组来保存信息的方法很常见也很有用),首先将所有的数字全初始化为0(素数),再将数字1与第一个素数2标记为1(非素数)从素数2开始将所有小于n的2的倍数都标记为1;继续该过程,将素数3的倍数筛掉,到循环结束时,标记仍为0的数就是素数。

代码如下:

//primer用来保存得到的素数
memset(check , 0 , sizeof(check) );
check[1] = 1;
int sum = 0,i,j;
for(i = 2;i <= n; i++)
{
	if(!check[i])
		primer[sum++] = i;
	for(j = i+1;j <= n;j += i)
		check[i] = 1;
}

这个算法的时间复杂度已经优化到O(nloglogn),但是仔细观察我们会发现,有些非素数会被我们筛去多次,比如6 会被 素数2筛去一次又会被素数3筛去一次。
这时候更好的欧拉筛法就来了。

欧拉筛法

欧拉筛法保证了一个合数只会被他最小的质数因子筛去一次。
(忽然想起来开学不久和肖xx讨论过这个筛法,这里就用当时聊天时的原话吧)
先给出代码

check[1] = 1;
int sum = 0,i,j;
for(i = 2;i <= n; i++)
{
	if(!check[i])
		primer[sum++] = i;
	for(j = i+1;j < sum;j ++)/*  1  */
		{
				if(i*primer[j] > n)
					break;
				check[i*primer[j]] = 1;
				if(i % primer[j] == 0)/*   2  */
					break;
		}
}

关键在于我在代码块中给出的两个注释。
因为任意一个合数都可以写成几个质数之积,也就是说任意一个合数都会有一个最小质因子。
所以当i可以整除primer[j]时,iprimer[j+1]一定已经被筛过了!(原因是i = primer[j] * x ,所以 iprimer[j+1]所筛去的数一定会被primer[j]筛去)

好了,说了这么多,是时候进入正题了!!
上题!
在这里插入图片描述
思路:
1.用线性筛法求出N范围内的素数保存在primer中(友情提示:大数组最好放在main函数之外定义,其他的题最好也是这样。为什么?百度“栈帧”,你就知道)
2.对这M组数据,用二分查找对比primer。

给出AC代码:

#include <stdio.h>
int n,w, primer[1000010],tot,s,i;
int check[100000010];
int main()
{
    scanf("%d%d",&n,&w);
    for(i = 2; i <= n; i++)
    {
        if(check[i] == 0) primer[++tot] = i;
        for(int j = 1; (j <= tot) && (i*primer[j] <= n); j++)
        {
            check[i * primer[j]] = 1;
            if(i % primer[j] == 0) break;
        }
    }
    while(w--)
    {
        scanf("%d",&s);
        i = 1;
        int j = tot, mid;
        while(i <= j)
        {
            mid = (i + j) / 2;
            if(s == primer[mid])
            {
                printf("Yes\n");
                break;
            }
            else if(s < primer[mid]) j = mid - 1;
            else i = mid + 1;
        }
        if(s != primer[mid]) printf("No\n");
    }
    return 0;
}

关于二分查找捏,下次遇见排序的题目是再提喽。
文中有错误或者不妥之处,还望各位大佬指正。

猜你喜欢

转载自blog.csdn.net/qm230825/article/details/86375992