C语言求一个大数范围以内的素数(质数)既然如此简单

⭐本章简介

本章讲解一下C语言求素数的一个程序。5种求解方式,最后一种可以求大数!
有多种方法,难度依次增大(但是不会太大),程序的质量也依次增大。

⭐求素数的方法

💡暴力求解

通过遍历除1以外小于欲求质数 x 的所有整数,如果有一个数可以被x整除。那么x就不是质数。

#include <stdio.h>

// 输入数 x 如果 x 是素数 就返回 true 否则就返回 false
bool IsPrimeNumber( int x );

int main(){
	int a=1,b=100;
	for( int i = a ; i <= b ; i++ ){
		// 如果他是素数就打印出来
		if( IsPrimeNumber(i) ){
			printf("%d  ",i);
		} else { }
	}
	
}

bool IsPrimeNumber( int x ){
	// 遍历除 1 以外所有比自己小的整数
	for( int i = x - 1 ; i > 1 ; i-- ){
		if( x%i == 0 ){
			// 如果 x 可以被 i 整除 那么就返回的 假 代表这个数不是素数
			return false;
		} else { /* 如果不能被 i 整除就继续遍历 */ }
	}
	// 如果除 1 以外所有比自己小的整数都不能被整除,那么他就是素数
	if( x <= 1 ){
		return false;
	} else {
		return true;
	}
}

这种方法有个特点 当 x 越大,那么 IsPrimeNumber 函数的运算时间也就越长。如果要求 一到100001以内的所有质数,要求好久才可以求得。
暴力求解
暴力求解 9w10w 以内的素数包括控制台输出时间费时2.699s

💡优化后的暴力求解

如果 1 到 x 以 内 的 整 数 \color{red}{1 到 \sqrt{x}}以内的整数 1x 均不能被 x 整除。那么后面就不可能有数被 x 整除 。这是因为如果 1 到 x 1 到 \sqrt{x} 1x 以内的数可以被整除的话,那么除出的结果一定是大于 x \sqrt{x} x 的。

#include <stdio.h>
#include <math.h>

// 输入数 x 如果 x 是素数 就返回 true 否则就返回 false
bool IsPrimeNumber( int x );

int main(){
	// 求 集合 [a,b] 中的所有素数
	int a=0,b=100;
	for( int i = a ; i <= b ; i++ ){
		// 如果他是素数就打印出来
		if( IsPrimeNumber(i) ){
			printf("%d  ",i);
		} else { }
	}
	
}

bool IsPrimeNumber( int x ){
	// 遍历除 1 以外所有比自己小的整数
	int sq = sqrt(x);
	for( int i = 2 ; i <= sq; i++ ){
		if( x%i == 0 ){
			// 如果 x 可以被 i 整除 那么就返回的 假 代表这个数不是素数
			return false;
		} else { /* 如果不能被 i 整除就继续遍历 */ }
	}
	// 如果除 1 以外所有比自己小的整数都不能被整除,那么他就是素数
	if( x <= 1 ){
		return false;
	} else {
		return true;
	}
}

这个方法比起暴力求解的计算时间久减少了很多。暴力求解需要的时间是t的话,那么这个方法求解的时间 大约就是 t \color{red}\sqrt{t} t 时间减少了很多。
优化后的暴力求解
优化后的暴力求解求 111w 包括控制台输出时间废时 2.813s

💡标记求解

利用一下两条定理:

  • 所有素数的倍数都不是素数
  • 非素数一定有一个约数是素数;
    这样子我们就可以定义一个列表 [0,n] 每求出一个素数x就把这个列表中所有x的倍数都去标记成非素数。那么没有标记的就是素数。又因为我们知道最小的素数是2,所以我们就从2开始标记起。
    而且有 优化后的暴力求解 可以知道,我们只要求出 [0, n \sqrt{n} n ] 以内的素数的时候排除掉所有素数的倍数,就可以排除掉 [0,n] 以内的所有素数了。
#include <stdio.h>
#include <math.h>

// 输入数 x 如果 x 是素数 就返回 true 否则就返回 false
bool IsPrimeNumber( bool *list , int x );
// 把 x 标志成素数
void SetPN( bool *list , int x );
// 把 x 标志成非素数
void ResPN( bool *list , int x );
// 把 list 中的素数标记出来
void GetPrimeNumber( bool *list , int b );


int main(){
	int a=90000,b=100000;
	//定义一个列表 为了方便 如果是 0 就是素数 不是 0 就不是素数
	bool list[1000000] = {1,1,0};
	// 循环遍历  [0,sqrt(n)] 
	GetPrimeNumber(list,b);
	for( int i = a ; i <= b ; i++){
		if( IsPrimeNumber(list,i) ){
			printf("%d ",i);
		} else { }
	}
	
}

void GetPrimeNumber( bool *list , int b ){
	for( int i = 2 ; i <= sqrt(b) ; i ++ ){
		// 如果 i 是素数的话 
		if( IsPrimeNumber( list  , i ) ){
			// 去除列表内 i 的倍数
			for( int j = 2 ;  ; j++ ){
				// 如果 i * j 超出需要求的列表范围就退出
				if( i*j > b ){
					break;
				}
				ResPN(list,i*j);
			}
		}
	}
}

bool IsPrimeNumber( bool *list  ,int x ){
	if(list[x]){
		return false;
	} else {
		return true;
	}
}

void SetPN( bool *list , int x ){
	list[x] = false;
}

void ResPN( bool *list , int x ){
	list[x] = true;
}

一定要注意 列表长度一定要远 要大于 b 否则容易出现意想不到的结果 ,这种求法实际上已经把 [0,n] 以内的所有质素求出来了。不仅仅是 [a,b]。
在这里插入图片描述

💡封装成对象

我们可以把 标记求解 封装成对象

#include <stdio.h>
#include <math.h>

class PrimeNumber {
		bool list[1000000] = {1,1,0};
	public:
		
		void GetPrimeNumber( int b ){
			for( int i = 2 ; i <= sqrt(b) ; i ++ ){
				// 如果 i 是素数的话 
				if( IsPrimeNumber( i ) ){
					// 去除列表内 i 的倍数
					for( int j = 2 ;  ; j++ ){
						// 如果 i * j 超出需要求的列表范围就退出
						if( i*j > b ){
							break;
						}
						ResPN(i*j);
					}
				}
			}
		}
		
		bool IsPrimeNumber( int x ){
			if(list[x]){
				return false;
			} else {
				return true;
			}
		}
		
	private:
		
		
		void SetPN( int x ){
			list[x] = false;
		}
		
		void ResPN( int x ){
			list[x] = true;
		}
};

int main(){
	int a=90000,b=100000;
	//定义一个列表 为了方便 如果是 0 就是素数 不是 0 就不是素数
	PrimeNumber p;
	// 循环遍历  [0,sqrt(n)] 
	p.GetPrimeNumber(b);
	for( int i = a ; i <= b ; i++){
		if( p.IsPrimeNumber(i) ){
			printf("%d ",i);
		} else { }
	}
}

💡利用 Bitmap 求解大数素数

虽然利用Bitmap会增大计算量,但是利用上面的技巧求解速度已经非常快了。利用BitMap可以求解大数的素数列表。

#include <stdio.h>
#include <math.h>

class PrimeNumber {
		unsigned char list[2000000] = {3};
	public:
		
		void GetPrimeNumber( unsigned int b ){
			for( unsigned int i = 2 ; i * i  <= b ; i ++ ){
				// 如果 i 是素数的话 
				if( IsPrimeNumber( i ) ){
					// 去除列表内 i 的倍数
					for( unsigned int j = 2 ;  ; j++ ){
						// 如果 i * j 超出需要求的列表范围就退出
						if( i*j > b ){
							break;
						}
						ResPN(i*j);
					}
				}
			}
		}
		
		bool IsPrimeNumber( int x ){
			if(Get_bit(list,x)){
				return false;
			} else {
				return true;
			}
		}
		
	private:
		
		
		void SetPN( int x ){
			Res_bit(list,x);
		}
		
		void ResPN( int x ){
			Set_bit(list,x);
		}
		
		bool Get_bit( unsigned char * st , unsigned int num ){
            return ( st[(int)(num/8)] & (0x01 << (num % 8) ) ) > 0 ? true : false ;
        }
        void Set_bit( unsigned char * st , unsigned int num ){
            st[(int)(num/8)] |= (0x01 << (num % 8) ) ;
        }
        void Res_bit( unsigned char * st , unsigned int num ){
            st[(int)(num/8)] &= ~(0x01 << (num % 8) ) ;
        }
		
};

int main(){
	unsigned int a=13999000,b=14000000;
	//定义一个列表 为了方便 如果是 0 就是素数 不是 0 就不是素数
	PrimeNumber p;
	// 循环遍历  [0,sqrt(n)] 
	p.GetPrimeNumber(b);
	for( unsigned int i = a ; i <= b ; i++){
		if( p.IsPrimeNumber(i) ){
			printf("%d ",i);
		} else { }
	}
}

下面是求解 13,999,000 一千三百九十九万九千14,000,000 一千四百万 的素数列表 。
利用Bitmap求解大数素数


13999003 13999009 13999057 13999079 13999099 13999121 13999133 13999147 13999159 13999169 13999171 13999189 13999213 13999253 13999267 13999273 13999289 13999291 13999301 13999309 13999351 13999369 13999393 13999397 13999399 13999421 13999423 13999441 13999459 13999463 13999471 13999477 13999481 13999519 13999529 13999537 13999549 13999597 13999603 13999621 13999651 13999673 13999691 13999703 13999723 13999729 13999747 13999757 13999759 13999781 13999787 13999813 13999831 13999871 13999873 13999877 13999889 13999919 13999957 13999961 13999969 13999981


如果不了解BitMap想了解的可以进我主页搜索BitMap。

👋都看到这里了,还不快点个关注?


✍️本文作者为 > 【谢玄.】 Mr-XieXuan < 于 2022/10/28/3:00 发布于 CSDN 。

📧E-mail: [ Mr_Xie_@outlook.com ]
⌨️GitHub: [ https://github.com/MR-XieXuan }
🔍个人私站: [ https://main.mrxie.xyz/ ]

  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢玄.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值