埃氏筛和欧拉筛

  2021年最后一天,刷题中学了两个素数筛法,埃氏筛和欧拉筛。

    一道洛谷题

题目截图
先简单说一下这道题目的思路,最开始的时候我是打算用题目中的方法,写了八个函数构造出八个不同位数的回文数然后挨个用暴力法判断每一个数是不是素数。WA了几个点就没管了,直接去看了题解,以后有机会可能可以来完善一下这个思路



看了题解的思路:欧拉筛+枚举

#include<bits/stdc++.h>
using namespace std;
int a, b; 
bool f(int a) {
  int t = a, s = 0;
  while(t) {
     s = 10*s + t % 10;
     t /= 10;
  }
  if(a == s)  return 1;
  else  return 0;
}
const int MAXN = 1e7+7;
bool vis[MAXN];          // 默认全部都是质数后面再进行筛选 
int prime[MAXN];
bool ans[MAXN];
void g() {
  memset(vis, 0, sizeof(vis));
  memset(prime, 0, sizeof(prime));
  memset(ans, 0, sizeof(ans));
  int cnt  = 0;
  for(int i = 2; i <= b; i++) {
    if(vis[i] == 0) {
      prime[cnt++] = i;
      ans[i] = 1; 
    }
    for(int j = 0; j < cnt && i * prime[j] <= b; j++) {
      vis[i*prime[j]] = 1; 
      if(i % prime[j] == 0) break;
      
    }
  }
}



int main() {
  cin >> a >> b;
  if(b > 10000000)  b = 9999999;
  g();
  for(int i = a; i <= b; i++) {
    if(f(i) && ans[i])
      cout << i << endl;
  }
  return 0;
}




再来一个小知识:一个数,奇数位上数字之和与偶数位上数字之和作差,如果差能被11整除,那么这个数就能被11整除。

证明如下(转自知乎):
在这里插入图片描述



埃氏筛:

就是一个素数的倍数一定不可能是素数,当然以这种思路构造出来的一定会有很多重复的比如:30可以看成2的15倍,3的5倍,5的三倍等等;

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e7; 
int prime[MAXN];  // 一开始默认为素数,1素数,0非素数。 

void fprime(int n) {
  memset(prime, 1, sizeof(prime));
  prime[0] = prime[1] = 0;
  for(int i = 2; i <= n; i++) {
    if(prime[i] == 0)  continue;
    for(int j = i * 2; j <= n; j += i) {
      prime[j] = 0;
    }
  }
  for(int i = 0; i <= n; i++) {
    if(prime[i])  cout << i << endl;
  }
}
int n;
int main() {
  cin >> n;
  fprime(n);
  
  return 0;
}






欧拉筛模板:

将复杂度优化为线性的方法

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e7;
int vis[MAXN];    //一开始默认为素数,0素数,1非素数。 
int prime[MAXN];  // 一开始默认为素数,0素数,1非素数。 
int ans[MAXN];    //数值为1的下标为对应的素数 
void fprime(int n) {
  int cnt = 0;     //计数器,不仅可以知道已经判断多少素数还可以用在后面优化 
  for(int i = 2; i <= n; i++) {
    if(vis[i] == 0) {
      prime[cnt++] = i;
      ans[i] = 1;
    } 
    for(int j = 0; j < cnt && j * prime[i] <= n; j++) {
      vis[i*prime[j]] = 1;
      if(i%prime[j] == 0)    break; //优化的关键,以一个数的最小质因数为准 
    }
  }
  for(int i = 0; i < cnt; i++) {
    cout << prime[i] << endl;
  }
}
int n;
int main() {
  cin >> n;
  fprime(n);


  return 0;
}




其中最为关键的优化步骤就是“if(i%prime[j] == 0) break; ”
分析如下:
假如i 可以被prime[j] 整除,
说明prime[j] i 的因子,
那么必然也是i的倍数的因子,
因此假如不跳出循环的话,
那么接下去,i*prime[j+1] 必然也是prime[j] 的某一倍数。
prime[j] 是比prime[j+1]更小的的因子

也就是说,num=k*prime[j]*prime[j+1] 时会被prime[j+1] 以i
倍标记。当 i=k*prime[j+1] 时,num会再次被prime[j]k*prime[j+1]倍标记,违反了欧拉的核心思想,所以才跳出循环。
注:num可以分解为多个质因子。

参考知乎:https://zhuanlan.zhihu.com/p/42609585
参考博客:https://blog.csdn.net/Lopka/article/details/103346341
https://blog.csdn.net/angrypop/article/details/83004962
https://blog.csdn.net/lady_killer9/article/details/88038624

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值