哥德巴赫猜想--普通方法及位运算高效方法

哥德巴赫猜想:任意一个不小于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);
}

两种哥德巴赫猜想的思想都是一样的,不同的是,第二种将所有质数先筛选出来,用的时候直接取用,并且运用位运算,使得时间复杂度大大减小,且用位存储数据,大大提高空间利用率,不过再用之前要注意“取位”的思想

这就是关于哥德巴赫猜想的所有想法,如果有不对的地方,欢迎指出(#^.^#)




猜你喜欢

转载自blog.csdn.net/szy2333/article/details/80596375