哥德巴赫猜想:任意一个不小于6的偶数总能写成两个素数之和
第一个实现的方法,直接上代码
#include <stdio.h>
#include <math.h>
#define boolean int
#define TRUE 1
#define FALSE 0
boolean isPrime(int num);
boolean lowGoldbachTest(int num);
boolean lowGoldbachTest(int arrange) {
int num;
int i;
boolean found;
boolean isGoldbachRight;
//从6开始,假设猜想是正确的,如果猜想正确且num小于验证范围,循环继续
for(num = 6, isGoldbachRight = TRUE; isGoldbachRight && num <= arrange; num +=2) {
for(i = 3, found = FALSE; !found && i <= num/2; i += 2) { //初始条件为未找到这组数,如果找到了,继续下一次循环
if(isPrime(i) && isPrime(num - i)) { //如果这两个数都是质数
printf("%d + %d = %d\n", i, num - i, num); //输出
found = TRUE; //把状态切换为找到
}
}
if(FALSE == found) { //如果没有找到一组数符合猜想
isGoldbachRight = FALSE; //状态变为猜想错误
printf("failed when test %d\n", num);
}
}
return isGoldbachRight;
}
boolean isPrime(int num) {
int maxNum = (int)(sqrt(num)); //取要验证的数的开平方
int i;
if(num > 2 && num % 2 == 0) { //如果这个数能被2整除,则一定是偶数,不肯能是质数
return FALSE;
}
for(i = 3; i <= maxNum && num % i; i += 2) { //从3开始,如果i始终小于maxNum且num不能被i整除,i+2,注意步长是2,
} //因为上一个循环已验证num不是偶数,因此+2就减少验证偶数的时间
return i > maxNum; //循环结束后,如果i>maxNum,则说明num是质数
}
int main() {
int arrange;
printf("please input the range you want to test:\n");
scanf("%d", &arrange);
lowGoldbachTest(arrange);
}
哥德巴赫猜想验证程序中,检验数值是否为质数的操作非常频繁,如果不优化,就无法提高运行速度
如果预先将“所有”质数全部找到,检测某个数是否为质数就会变得简单高效
首先,将相当大范围的数,全部存储到数组中,或者说,有一个数组,里面有n个数,这n个数就是我们要处理的数据。定义一个有n个数的数组,那么,其下标就是0~n-1,将值要求为0或1,0表示非质数,1表示质数,令数组名为numSet
0 0 1 1 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
在上述数组存在的情况下,判断一个在20以内的正整数num是否为质数,就这样实现
return sunSet[num]; 赋值:int numSet[1000] = {0};
接下来,完成素数的筛选,基本思想:对于当前为0的数组(其下标就是该数值本身,0表示未处理,用0作为质数的标志)
对于该数,将它的所有倍数变为1,即非质数
index = 2;
while(index <= (int) sqrt(N)) {
if(0 == numSet[index])
for(i = index*index; i < N; i += index) {
numSet[i] = 1;
}
}
index++;
}
在实现位运算实现哥德巴赫猜想之前,需要先说明关于位运算的知识
如果用一个位表示一个数值,且一个位的数值就是0或1,用0表示质数,用1表示非质数
关键点是我们要找到数值到底是那个字节中的哪个位
0 1 2 3 4 5 6 7
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
1 2 3 4 5 6
对于35,应该对应出两个数值 : 字节下标和位下标
35/8 字节下标 35%8 位下标
在这个问题完成之后,另外两个基本问题是:判断那一位是0或者1;将那一位数置1
#define GET_BYTE(vbyte, index) (((vbyte) & (1 << ((index) ^ 7))) != 0) 判断那一位数的值
#define SET_BYTE(vbyte, index) ((vbyte) |= (1 << ((index) ^ 7))) 将那一位数置1
#define CLR_BYTE(vbyte, index) ((vbyte) &= (~(1 << ((index) ^ 7)))) 将那一位数置0
由于位运算存在“用符号位补位”的问题,所以,必须是unsigned char类型,以避免不必要的错误
最后一个问题,若需要10000个数,那么,应该申请多少个字节呢?
10000 / 8 = 1250
但是,如果给的数(假设为arrange)不能被8整除呢? --> (arrange + 7) / 8
实现代码:
#include <stdio.h>
#include <math.h>
#include <malloc.h>
#define GET_BYTE(vbyte, index) (((vbyte) & (1 << ((index) ^ 7))) != 0) //判断那一位数的值
#define SET_BYTE(vbyte, index) ((vbyte) |= (1 << ((index) ^ 7))) //将那一位数置1
#define CLR_BYTE(vbyte, index) ((vbyte) &= (~(1 << ((index) ^ 7)))) //将那一位数置0
#define boolean int
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1
typedef unsigned char u8;
u8 *set = NULL; //建立一个动态数组
boolean isPrime(int num); //判断是否是质数
boolean highGoldbach(int arrange); //进行哥德巴赫猜想验证
void setPrime(int arrange); //将所有质数筛选出来
void setPrime(int arrange) {
int maxNum = (int) sqrt(arrange);
int i = 3;
int j;
set = (u8 *) calloc(sizeof(u8), (arrange + 7) >> 3); //申请一个动态数组空间
for(j = 4; j < arrange; j += 2) {
SET_BYTE(set[j >> 3], j & 7); //从4开始,将所有偶数置1
}
while(i <= maxNum) {
if(0 == GET_BYTE(set[j >> 3], j & 7)) { //如果这个数是质数或者未处理
for(j = i*i; j < arrange; j += (i << 1)) {
SET_BYTE(set[j >> 3], j & 7); //将这个数的倍数置1
}
}
i += 2;
}
}
boolean highGoldbach(int arrange) {
int num;
int i;
boolean found;
boolean isGoldbachRight;
//从6开始,假设猜想是正确的,如果猜想正确且num小于验证范围,循环继续
for(num = 6, isGoldbachRight = TRUE; isGoldbachRight && num <= arrange; num +=2) {
for(i = 3, found = FALSE; !found && i <= num/2; i += 2) { //初始条件为未找到这组数,如果找到了,继续下一次循环
if(isPrime(i) && isPrime(num - i)) { //如果这两个数都是质数
printf("%d + %d = %d\n", i, num - i, num); //输出
found = TRUE; //把状态切换为找到
}
}
if(FALSE == found) { //如果没有找到一组数符合猜想
isGoldbachRight = FALSE; //状态变为猜想错误
printf("failed when test %d\n", num);
}
}
return isGoldbachRight;
}
boolean isPrime(int num) {
return 0 == GET_BYTE(set[num >> 8], num & 7);
}
int main() {
int num;
printf("请输入要验证的范围:\n");
scanf("%d", &num);
setPrime(num);
highGoldbach(num);
}
两种哥德巴赫猜想的思想都是一样的,不同的是,第二种将所有质数先筛选出来,用的时候直接取用,并且运用位运算,使得时间复杂度大大减小,且用位存储数据,大大提高空间利用率,不过再用之前要注意“取位”的思想
这就是关于哥德巴赫猜想的所有想法,如果有不对的地方,欢迎指出(#^.^#)