素数筛法

素数定理\(\pi(x) = \frac{x}{\ln(x)}\),即不超过 \(x\) 的素数的个数接近于 \(\frac{x}{\ln(x)}\)

朴素筛

void isPrime(int n)
{
	for (int i = 2; i <= n; ++ i)
	{
		k = sqrt(i); flag = false;
		for (int j = 2; j <= k; ++ j)
		{
			if (i%j == 0) 
			{
				flag = false;
				break;
			}
		}
		if (flag) isPrime[++ PrimeSize] = i;
	}
}

埃拉托斯特尼筛法(埃氏筛)

算法过程:

以最小的素数 \(2\) 为起始,作为一个合数的 较小 的因数,筛掉 \(2^2-\dots\) 的合数,然后判断下一个没有被筛掉的数(即为素数),重复进行即可。

注意: 从一个质数的 平方 开始筛。

时间复杂度为 \(O(n\log(\log n))\)推导过程)。

优化:

(1)当用 \(\sqrt n\) 筛的时候,已经是可以筛到 \(n\) 这个数字了。另外,在 \(\sqrt n\)\(n\) 的数中,它的最小因数一定是小于等于 \(n\) 的,所以枚举时,只需枚举到 \(\sqrt n\) 即可。

当然 还有许多优化:(2)只筛奇数、(3)分块筛选 \(\dots\dots\)

实现

void AISHI(int n)
{
	m = sqrt(n);
	memset(Prime,true,sizeof Prime);
	for (int i = 2; i <= m; ++ i)
	{
		if (Prime[i]) isPrime[++ isPrimeSize] = i;
		for (int j = i*i; j <= n; ++ j)
		{
			Prime[j] = false;
		}
	}
}

线性筛(欧拉筛法)

前提:

在埃氏筛中,一个合数可能会被它的多个质数因数 筛掉多次,例如:\(12\) 会被 \(2\)\(3\) 筛掉,相当于其他合数,被筛掉多次的次数大大增加。

为了减少这种损失,最好是用 最小 的因数 去筛掉这个数。

线性筛也用于 欧拉函数 的求解过程中,也称作 欧拉筛法

实现过程:

\(i\) 作为某一个合数的 最大 因数,枚举比 \(i\) 小的所有质数 \(k_j\),作为最小因数,将 \(i\times k_j\) 筛掉。

又因为 \(i\) 可能为一个合数,所以当出现 \(i\bmod k_j = 0\) 的时候,\(i\) 中存在一个与 \(k_j\) 相等的因数。那么在枚举比 \(k_j\) 大的质数 \(k_l\) 时,\(k_l\) 就不是 \(i\times k_l\) 的最小因数了,存在一个数 \(y=\frac{i}{k_j}\) 使得 \(i*k_l = (y*k_j)\times k_l\),由此就可以将 \(i\) 的循环结束掉。

那么 \(k_l\times i\) 这个合数,一定会在 \(k_j\times (\frac{i}{k_j}\times k_l)\) 的时候被筛掉,此时 \(k_j\) 将会作为最小因子存在,而 \(\frac{i}{k_j}\times k_l\) 一定是一个比 \(i\) 大的数,所以不必担心漏掉。

void OULA(int n)
{
	memset(Prime,true,sizeof Prime);
	for (int i = 2; i <= n; ++ i)
	{
		if (Prime[i]) isPrime[++ isPrimeSize] = i;
		for (int j = 1; j <= isPrimeSize && i*isPrime[j] <= n; ++ j)
		{
			Prime[i*isPrime[j]] = false;
			if (!(i%isPrime[j])) break;
		}
	}
}
posted @ 2023-01-10 11:58  Ciaxin  阅读(98)  评论(0编辑  收藏  举报