埃氏筛法和线性筛

埃氏筛法和线性筛

埃氏筛法

如果一个数是合数,那么它一定有素数因子;

证明:反证法,如果一个数是合数,且其所有因子都是合数。 ----------------------(1)

如果(1)式正确,那么 4 为合数,其因子应为合数,但是 2 显然为素数,所有(1)式不成立;

埃氏筛法就是将 素数 的倍数全部筛掉

代码如下

const int N = 1e6 + 10;
int primes[N], ans;					//全局变量 自动赋值为 0
bool st[N];                           //全局变量 自动赋值为 false

void get_primes(int n) {
	for (int i = 2; i <= n; ++i) {
		if (!st[i]) {
			primes[ans++] = i;
			for (int j = i + i; j <= n; j += i) {
				st[j] = true;
			}
		}
	}
}

问题

看到这段代码,不知道你会怎么想,是不是也恰好有一下几个问题呢?

  1. 为什么 n2 开始循环
  2. if 判断语句 () 里面的 !st [i] 是什么意思
  3. primes[ans++] = i 是什么意思
  4. 第九行 for 循环里面的 j 为什么初始化为 i+i
  5. 为什么 j += i

解答

  1. 因为 10 都不是素数;在判断之前,我们在初始条件的时候,默认的所有数都是素数;
  2. 因为我们默认所有数字都是素数,但是,bool 数组却默认初始化为 false ===> , 所有用取反 来表示我们的原本的意思
  3. 如果这个数是素数,也就是这个数没被其他素数筛掉,我们就认为它是素数,就把他存到数组里面
    1. 这两条都是说,我们要把素数的倍数全部筛掉。

新问题

我们发现,埃氏筛法虽然无限逼近线性 O(n),但它却不是线性的,因为它还有可以 优化 的地方

  • 当我们其中一个合数 6 进行分析的时候,我们不难发现,6 既被 2 筛一次,又被 3 筛一次;
  • 如果继续观察其他合数,我们可以发现,大多数的合数的素数因子不唯一
  • 那么问题来了,有没有一种方法可以让一个合数只被它的最小质因子筛掉呢?

答案是有的,线性筛!

在这之前,需要补充一下上面题解的 java 版本

class fun{
    int[] primes = new int[1000010];
    boolean[] st = new boolean[1000010];
    int ans;
    public void get_primes(int n){
        for(int i=2; i<=n; ++i){
            if(!st[i] ){
                primes[ans++] = i;
                for(int j=i+i; j<=n; j+=i){
                    st[j] = true;
                }
            }
        }
    }
}

线性筛

在阅读本部分之前,请确保你已经看懂了埃氏筛法

先上代码

const int N = 1e6 + 10;
int primes[N], ans;
bool st[N];

void get_primes(int n) {
	for (int i = 2; i <= n; ++i) {
		if (!st[i]) {
			primes[ans++] = i;
		}
        //重点部分
        //start
		for (int j = 0; primes[j] <= n / i; ++j) {
			st[primes[j]*i] = true;
			if (i % primes[j] == 0) {
				break;
			}
		}
        //end

	}
}

重点部分的理解

  1. j 为什么从 0 开始
  2. 为什么要 primes[j] <= n / i
  3. 这句话什么意思 *st[primes[j]i] = true
  4. 为什么要判断这句话 if (i % primes[j] == 0)

解答部分

  1. 因为我们要从最小的素数开始,将两数乘积的所有值都筛掉。之所以从 0,也是为了满足,每个合数只被它最小的质因子筛掉

  2. 这取决于我们筛数的范围 st[ ] 里面的值不难大于 n

    st[primes[j]*i] = true
    
  3. 在这个问题上,我发现语言竟是如此的缺乏说服力,建议拿草稿纸演算一下。看看是否解决了 6 被 2 和 3 除两遍的问题

    for (int j = 0; primes[j] <= n / i; ++j) {
    			st[primes[j]*i] = true;
    			if (i % primes[j] == 0) {
    				break;
    			}
    		}
    
  4. (1.)当 i % primes[j] == 0 成立, 则 primes[j] 一定是 i 的最小质因子;所有 primes[j] 也一定是 i * primes[j] 的 最小质因子

    (2.)当 i % primes[j] == 0 不成立,则 primes[j] 一定是 i ***** primes[j] 的最小质因子,因为 j 是从 0 开始循环的;

那么为什么当判断语句成立的时候我们要跳出循环呢

假设 i = x * primes[j] ,如果不跳出循环,则下一个被筛掉的数 primes[j+1] i 的最小质因子一定不是 primes[j+1] 而是primes[j]* ,就不符合条件。

java实现(面向数据编程)

class fun{
    int cnt;
    boolean[] st = new boolean[179424822];
    public void get_prime(int[] prime){
        for (int i = 2; i <= 179424821; i ++ )
        {
            if (!st[i]) prime[cnt ++ ] = i;
            for (int j = 0; prime[j] <= 179424821 / i; j ++ )
            {
                st[prime[j] * i] = true;
                if (i % prime[j] == 0) break;
            }
        }
    }
}

完结撒花!

  • 12
    点赞
  • 12
    收藏
  • 打赏
    打赏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论 21

打赏作者

codezzzsleep

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值