欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【笔记】 素数筛法(朴素筛法,优化筛法,线性筛法)

程序员文章站 2024-03-14 21:30:29
...

素数在OI中的应用很广

很多题都会用到素数,那么如何得到素数就是件很重要的事了

暴力枚举并判断?当然可以但实在太暴力了一点

考虑到如果一个数i是素数,那么i的倍数显然不可能是素数

由此我们可以通过不断的筛去不是素数的数,从而得到剩下的素数

----------------------------------------------------------------------------------------------

算法1.朴素算法:

假设我们要得到1~10中的素数

那么首先我们已知1不是素数,最小的素数为2,先列出一个1~10的数组

1 2 3 4 5 6 7 8 9 10

直接将1划去得到:

1 2 3 4 5 6 7 8 9 10

从2开始筛去它的倍数:

1 2 3 4 5 6 7 8 9 10

然后我们向后接着找是素数的数,继续以3来筛去它的倍数:

1 2 3 4 5 6 7 8 9 10

枚举到4发现它不是素数,直接跳过,一直枚举到10为止,我们就得到了一个有标记的数组

1 2 3 4 5 6 7 8 9 10

最后剩下的数(即标红的数)就是我们所要的素数了

从这一个例子开始,我们可以轻松地推广至求1~n的素数

那么只需从2开始枚举至n并删去素数的倍数就可以了

计算量为n/2+n/3+n/4+...,是一个调和级数,时间复杂度为O(nlogn)

代码如下:

void is_prime (int n)
{
	memset (vis, 0, sizeof (vis));
	for (int i = 2; i <= n; ++i)
		for (int j = i + i; j <= n; j += i)
			vis[j] = 1; //vis[]用来判断是否为素数,打上标记的都为合数 
}

---------------------------------------------------------------------------------------------------------------------------------

算法2.优化算法:

我们注意到朴素算法中比如36会被2,3,4,6,9,12,18这6个数筛到,相当于被2*18,3*12,4*9,6*6,9*4,12*3,18*2筛到,实际上

a*b和b*a是等价的,于是,我们可以通过枚举小的那个因子,来优化一半的计算量,复杂度不变为O(nlogn),常数上优化了

代码如下:

void is_prime (int n)
{
	int up = (double) sqrt(n*1.0) + 1;
	for (int i = 2; i <= up; ++i)
		for (int j = i * i; j <= n; j += i)
			vis[j] = 1;
}

---------------------------------------------------------------------------------------------------------------------------------

算法3.线性筛法:

虽然优化算法的复杂度已经不是那么大了,但我们发现,就算枚举小的因子,一个数x依旧会被1或多个数筛过,

即对一个数有重复的筛去,所以我们考虑怎么样能够让一个数只被筛去一次呢?

那么便是我们的线性筛法,我们考虑筛去一个数时,只由它的最小素因子来进行,就可以做到O(n)的筛素数

代码如下:

void is_prime (int n)
{
	for (int i = 2; i <= n; ++i)
	{
		if (!vis[i])
			p[++cnt] = i;
		for (int j = 1; j <= cnt; ++j)
		{
			if (i * p[j] > n)
				break;
			vis[i * p[j]] = 1; //p[j]是i*p[j]的最小素因子 
			if (i % p[j] == 0) //再往后p[j]就不是最小素因子了 
				break;
		}
	}
}
相关标签: 筛素数