哥德巴赫猜想:任意一个偶数都等于两个质数之和,求质数对个数

8 篇文章 2 订阅

题目描述

​ 有一天路飞突发奇想,他有一个猜想,任意一个大于 2 的偶数好像总能写成 2 个质数的和。路飞查了资料,发现这个猜想很早就被一个叫哥德巴赫的人提出来了,称为哥德巴赫猜想。目前还没有证明这个猜想的正确性。路飞告诉你一个整数 n ,让你用这个数去验证。

​ 注意 1 不是质数。

输入
输入一个偶数 n(2≤n≤8000000)​ 。

输出
输出一个整数表示有多少对 (x,y) 满足 x+y=n(x≤y) 且x,y 均为质数。

样例输入1
10
样例输出1
2
数据规模与限定
时间限制:1 s

内存限制:64 M

直观方案:

#include <stdio.h>
#include <math.h>
int is_prime(int n){
	for(int i=2,I=sqrt(n);i<=I;i++){
		if(n%i) continue;
		return 0;
	}
	return 1;
}

int main(){
	int n,count=0;
	scanf("%d",&n);

	for(int i=2;i<=n/2;i++){
		if(is_prime(i)&&is_prime(n-i)) count++;
	}
	printf("%d",count);
	return 0;
}

对每个数都需要循环判断是否为质数,效率很低,容易超时:
在这里插入图片描述

分析

给的测试数据比较大,得从其他角度思考。

方案一:

给定数组primes,每一位初始化为0代表为质数,从i=2开始,当遇到0时,将2 * 2,2 * 3,2 * 4位置标记为1,依次类推,最终得到得primes数组,质数会被标记为0,合数标记为1。比如primes[2]=0代表2为质数,primes[9]=1代表9为合数。如果primes[i]=primes[n-i]=0,代表找到一组质数对,他们的和等于n。
代码如下:

#include <stdio.h>
#include <math.h>
#define MAX 8000000
int primes[MAX]={0};
void mark_primes(int n){	
	for(int i=2;i<=sqrt(n);i++){
		if(!primes[i]){
			for(int j=i*i;j<=n;j+=i){
				primes[j]=1;
			}
		}
	}
}


int main(){
	int n,count=0;
	scanf("%d",&n);
	mark_primes(n);
	for(int i=2;i<=n/2;i++){
		if(!primes[i]&&!primes[n-i]) count++;
	}
	printf("%d",count);
	return 0;
}

结果:
在这里插入图片描述

方案二

  1. 可以先利用素数筛,找到n以内的质数,得到primes数组
  2. 再循环查找,比如输入n为100时,2为质数,那么98是否在primes中(此时可以用二分查找)。

备注: 循环查找时,并不需要遍历整个primes,因为两个数之和如果等于n,那么必有一个小于等于n/2,另一个大于等于n/2。所以可以将primes根据n/2分成较小数部分和较大数部分,循环小数组部分得到质数1,在大数部分寻找质数2(n-质数1),如果能找到质数2,则所求质数对 +1.

代码实现:

#include <stdio.h>
#define MAX 8000000
int primes[MAX]={0};
//优化的素数筛,最后得到primes,首位表示n以内的质数总个数,后面依次为对应的质数2,3,5,7……
void get_primes(int n){	
	for(int i=2;i<=n;i++){
		if(primes[i]==0) primes[++primes[0]]=i;
		for(int j=1;i*primes[j]<=n;j++){
			primes[i*primes[j]]=1;
			if(i%primes[j]==0) break;
		}
	}
}

//二分查找,用于查找质数2是否在primes的后半段中
int bin_search(int *nums,int target,int len){
	int left=0,right=len-1,mid=0;
	while(left<=right){
		mid=left+((right-left)>>1);
		if(nums[mid]==target) return mid;
		if(nums[mid]>target) right=mid-1;
		else left=mid+1;
	}
	return -1;
}

//寻找右边界,用于查找数组中小于等于n/2的索引位置
int bin_search_right(int *nums,int target,int len){
	int left=0,right=len,mid=0;
	while(left<right){
		mid=left+((right-left)>>1);
		if(nums[mid]>target) right=mid;
		else left=mid+1;
	}
	return left-1;
}
int main(){
	int n,count=0;
	scanf("%d",&n);
	get_primes(n);
	int flag=bin_search_right(&primes[1],n/2,primes[0])+1; //flag 将primes分为两部分,前部分小于等于n/2,后部分大于等于n/2(因为可以取两个相等的质数,所以左右区间都应该取n/2);计算边界时左边界从0开始,但是实际primes中的素数从1开始,所以最后使用flag时需要+1.
	for(int i=1;i<=flag;i++){
		if(~bin_search(&primes[flag],n-primes[i],primes[0]-flag+1)) count++; //在右边能找到质数2,则count++,右边部分的长度等于总长度-左边+1(加1是因为重复取了flag边界值),即“primes[0] - flag+1
	}
	printf("%d",count);
	return 0;
}

结果:
在这里插入图片描述

小结

可以看到方案2的代码量要大于方案1,切运用到了二分查找,素数筛的方法。但是内存和时间都略优于方案1.

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值