拓展上机-3题解:哥德巴赫猜想
哥德巴赫猜想
方法一:试除法
注意:
- i ≤ x i i≤\frac{x}{i} i≤ix是最好的写法。
- 如果是写成 s q r t ( x ) sqrt(x) sqrt(x),每次判断 i i i是否小于 s q r t ( x ) sqrt(x) sqrt(x)都要调用 s q r t sqrt sqrt这个函数,造成时间的浪费。
- 也有写成 i × i ≤ x i\times i≤x i×i≤x的,这种写法也不好,如果 i × i i\times i i×i的结果超数据范围了,那么就会变成负数。
#include<stdio.h>
#include <stdbool.h>
#include<math.h>
#define N 10000
bool vis[4 * N];//false表示是素数,否则不是素数
int prime[N];//存储素数
int cnt = 0;//下标,同时可以计算素数的个数
int main() {
int x;
while (scanf("%d", &x)) {
if (x == 0) break;
int cp = 0;//统计素数对
for (int i = 2; i <= x / 2; i++) {
//判断i和x-i是否都是素数
int sign = 0;
for (int j = 2; j <= i / j; j++) {
if (i % j == 0) {
sign = 1;
break;
}
}
if (sign == 1) continue;
for (int j = 2; j <= (x - i) / j; j++) {
if ((x - i) % j == 0) {
sign = 1;
break;
}
}
if (sign == 0) cp++;
}
printf("%d\n", cp);
}
return 0;
}
方法二:埃式筛
思想:质数的倍数一定不是质数,一个合数(非质数)一定能被质数整除。
补充:
- 质数定理: 1 − n 1-n 1−n中有 n l n n \frac{n}{ln n} lnnn个质数
- 时间复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
#include<stdio.h>
#include <stdbool.h>
#include<math.h>
#define N 10000
bool vis[4 * N];//false表示是素数,否则不是素数
int prime[N];//存储素数
int cnt = 0;//下标,同时可以计算素数的个数
int main() {
int n = 2 << 14;
for (int i = 2; i <= n; i++) {//判断2-n中的素数
if (!vis[i]) prime[cnt++] = i;//存储素数
for (int j = i + i; j <= n; j += i) {
vis[j] = true;//所有素数的倍数都不是素数
}
}
int x;
while (scanf("%d", &x)) {
int cp = 0;
if (x == 0) break;
for (int i = 0; prime[i] <= x / 2; i++)
if (!vis[x - prime[i]]) cp++;
printf("%d\n", cp);
}
return 0;
}
方法三:线性筛
若 n ≈ 1 0 6 n≈10^{6} n≈106,线性筛和埃氏筛的时间效率差不多,若 n ≈ 1 0 7 n≈10^{7} n≈107,线性筛会比埃氏筛快了大概一倍。
线性筛这个算法的核心在于筛质数的时候规定一个数只能被它的最小质因数筛掉。
#include<stdio.h>
#include <stdbool.h>
#include<math.h>
#define N 10000
bool vis[4 * N];//false表示是素数,否则不是素数
int prime[N];
int cnt = 0;
int main() {
int n = pow(2, 15);//2 << 14
for (int i = 2; i <= n; i++) {//先求出所有的素数
if (!vis[i]) prime[cnt++] = i;//是素数
for (int j = 0; prime[j] <= n / i; j++) {//剔除掉素数的倍数,也就是非素数
vis[prime[j] * i] = true;
if (i % prime[j] == 0) break;
//这一步是干什么的呢,如果i % prime[j] == 0,prime[j]<i(一定)
//prime[j]只能是i的最小质因数,也是i*prime[j]的最小质数
//prime里面的质数是由小到大存储的,如果只有当前的prime[j]能整除i,那么先前的prime都不能,所以先前的prime都不是i的因数,而后面的prime[j]也不可能作为i的最小质因数因为后面的prime比当前的prime[j]大
//可以直接break了
//当i % prime[j] != 0时,也就是说prime[j]比i的最小质因数小,且prime[j]是prime[j]*i的因数,所以prime[j]就是prime[j]*i的最小质因数
}
}
int x;
while (scanf("%d", &x)) {
if (x == 0) break;
int cnt = 0;
for (int i = 0; prime[i] <= x / 2; i++)
if (!vis[x - prime[i]]) cnt++;
printf("%d\n", cnt);
}
return 0;
}