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