求素数的几种方法

首先介绍以下素数的定义

素数,也称为质数,其只能被1或者自身整除的自然数(不包括1)。换而言之,只有两个正因数的自然数称为素数。

与之相对的比1大但不是素数的自然数称为合数。1和0既不是素数也不是合数,合数由若干个质数相乘得到。

 基础方法:

显然根据定义就能判断一个数N是否为素数,具体的,对其从2到sqrt(N)进行除法,判断是否存在余数为0的情况,存在即认为N为合数,否则即为素数(这里临界的根号N是因为,对于N=p×q,显然p和q至少有一个小于根号N)。

bool prime(int n){
	int i = 0, j = 0;
	if (n < 2) return 0;
	for (i = 2; i <= sqrt(n); i++) {
		if (n%i == 0)
			return 0;
	}
	return 1;
}

埃氏筛:

埃拉托斯特尼筛法,简称埃氏晒,是一种用来求自然数n以内的全部素数。

他的基本原理是,如果我们要获得小于n的所有素数,那就把不大于根号n的所有素数的倍数剔除。

埃氏晒的原理很容易理解,一个合数,必然可以表示成,一个自然数 i 和一个素数的乘积。因此我们找到一个素数后,把他小于n的倍数全部标记为合数,这就是我们要做的

bool flag[N_MAX];//0为素数 1为合数
int primes[N_MAX];

void prime(int n) {
	bool flag[MAX];//0为素数 1为合数
	memset(flag, 0, sizeof(flag));
	for (int i = 2; i * i < n; i++) {
		//i为素数
		if (flag[i] == false) {
			for (int j = i * i; j < n; j += i) {
				//将其倍数标记为合数
				flag[j] = true;
			}
		}
	}
}

值得注意的是,其中内层循环的起始点为 i×i,这是因为 i × (2 至( i –1))在之前的循环中,已经被筛去(因为2 至( i –1)均小于i)。观察被筛掉的数据,我们可以发现,一个合数,可能会被筛掉多次,例如,30 = 2 * 15 = 3 * 10 = 5 * 6,所以30 就至少被2,3,5这三个质数分别筛过了,这个地方就会造成时间的浪费。

线性筛(欧拉筛):

 该方法保证每个合数仅被筛一次,其依据如下:

每个合数n都可以表示成这种形式,其中Factormax表示n的最大因数,并且剩下的这个 P 满足:

  1. 它是一个素数.
  2. 它比 Factormax的所有因数都要小.

即 P是 n 的最小素因数.

证明如下:

  1. 假设P为合数,则P = P1 * P2 * ···· * Pn,其中,P1 Fatory,所以和Factory是n的最大的因数矛盾,故假设不成立
  2. 假设P大于Factory的某个因数,不妨设P>P1因为 Factory = P1 * P2 * ···· * Pn 因此 P * P2 * ··· * Pn 必然大于Factory 所以和Factory是n的最大因数矛盾,故假设不成立

假设有一个素数表 prime[n]和一个是否为合数的标记表 flag[n] (定义 true表示合数)。我们每次枚举一个数 i,判断它是否标记过,如果是,那么存入 prime[n] ,否则不存。接下来我们标记由 i (不论它是素数与否)产生的数 n= i⋅prime[n]为合数,因为显然质数和另一个数(>2 )相乘为合数。

根据以上结论,我们对于一个数,把它作为另一个数最大因子,那么显然可以根据已有的质数表产生一些合数. 并且我们知道这些合数显然只有一种产生方法(只有一个 Factormax)那么我们每次根据枚举到的 i 只要判定prime[n]是不是最小素因数就可以保证枚举的量不多不少。

在判断 i % prime[n] == 0 之前的素数均为对应合数的最小素因数,其后的素数均不是。这是因为 i 可以分解为 prime[n] × k,因此对于后面的更大的素数prime[n+1]来说,其对应的合数prime[n+1] × i = prime[n+1] × prime[n] × k,其中 prime[n]小于prime[n+1],故prime[n+1]不是该合数的最小素因数。

bool flag[N_MAX];//0为素数 1为合数
int primes[N_MAX];	//存放找到的素数

void euler_prime(int n) {
	int tot = 0;
	memset(flag, 0, sizeof(flag));

	for (int i = 2; i <= n; i++) {
		if (!flag[i])	//尚未被置为1,说明为素数
			primes[tot++] = i;	//保存该素数
		// 以当前值i作为最大因数,找出所有对应合数
		for (int j = 0;j 

 

你可能感兴趣的:(数论,LeetCode,C++)