ACM:数论专题(1)——素数的判定

   (P.S: God!!!!!! 当前人类对于数学的研究所达到的高度已经远远超出了一个外行人所理解的程度, 俺是这么想的。。。。。。 )
题目要求:
    题目要求给定一个数字n,判定其是否为素数(n≤10^18)。素数:对于某个数字n,如果在所有不大于n的正整数中,只有1和n本身可以整除n,则n是一个素数。

解答:
    素数的判定具有多种方法。
    最朴素的方式是:利用循环枚举所有不大于n的正整数,判定每个正整数是否可以整除n,进而判定待测数字是否为素数。
    朴素的算法可以进行改进,以提高其性能:首先,除2以外的所有的偶数一定不是素数,其次在枚举数字时,只要枚举到sqrt(n)即可,而从sqrt(n)到n-1的数字一定不能整除n,这样,算法的性能就能大幅度改善。
    但是对于本题,在n∈[2, 10^18]的庞大的范围内,以上的算法均会超时,这里介绍一种称为Miller-Rabin的判定素数的算法,这是一个基于概率的算法。
    首先需要了解两个定理:
    1、费马小定理:对于素数p和任意的整数a(a≠0),有:

    以上定理说明,如果一个数字p是素数,那么对于任意的整数a,一定满足a^p = a % p,即a的p次幂除以p和a本身除以p后得到的余数是相同的。如果把上面的式子两边同时除以a,那么就可以得到:
 
    费马小定理给出了一个素数的性质,所有的素数都满足这样的性质。但该定理的逆定理却不一定成立,满足这样的性质的数字有很大的可能性是一个素数,但却不能保证是素数。
    2、二次探测定理:Miller和Rabin在费马小定理的基础上提出了二次探测定理: 如果一个数字p是奇素数(即:除2以外的素数),那么对于方程:
    
它的解一定是下面的两个:
    
    二次探测定理同样只是给出了一个素数的性质。反之满足上面的性质的数字有很大的可能性是素数,但不能确保一定是素数。
    (p.s:上面的两个定理如果有大牛知道怎么证明,记得留言告诉我。。。
    
    Miller-Rabin素数判定算法就是根据上面的两个定理提出的,对于一个数字p,要判定其是否是素数,该算法给出了一下的步骤:
    (1)、随机找一个数字a,计算s=a^(p-1)%p的值,如果该值不是1,则可以判定该数字一定不是素数,算法那结束;否则:执行步骤(2)。
    (2)、如果步骤(1)得到的结果是1,那么令:u=p-1,如果p-1是一个偶数,那么u/2就是一个整数,于是就可以得到下面的式子:

于是:x = a^((p-1)/2) 就是方程:x² % p = 1的一个解,再根据二次探测定理就可以得到:
 
  
所以, 本步骤我们计算a^(u/2)的值,如果这个值既不是1,也不是p-1,那么就不满足二次探测定理,数字p一定不是素数,算法结束;否则执行步骤(3)。
    (3)、如果步骤(2)的计算结果是1,并且此时u/2也是一个偶数,那么让u减半,继续执行步骤(2);否则,本算法结束,我们得到的结论是:数字p可能是素数。

    根据以上算法步骤的描述,我们得到的结果只有两种:待测数字不是素数或者待测数字可能是素数,并不能给出一个确切的结论即:待测数字一定是素数。因此在利用Miller-Rabin算法检测素数时,需要多次随机为数字a取不同的值进行检测,就可以大大减少误判的概率。
    通过相关的数学推导可以证明:如果取不同的a值,对一个数字进行t次Miller-Rabin检测,那么它的误判率可以控制在0.25^t以下。

    以上就是Miller-Rabin素数判定算法的原理。
    
    最后给一个判定素数的小技巧:如果待测数字n < 2^64,那么,只要选取:2 3 5 7 11 13 17 19 23 29 31 37 这些数字进行判定即可,如果待测数字对以上12个数字都不能整除,那么,就可以判定它是素数。

输入输出格式:
    输入:输入第一行为一个数字t,代表有t个测试数字;接下来的t行,每行是一个数字A。 
    输出:对于每一个输入的数字A,如果它是素数,输出Yes,否则输出No。

数据范围:
     10≤t≤50

     2 ≤A≤10^18


程序代码:

#include
#include
#include
#include


/*
 * This function uses Miller-Rabin algorithm to check if the 
 * number is prime.
 * Parameters:
 *		@n: The number to check.
 * Returns:
 *		If the number is prime number returns 1, or returns 0.
 */
int miller_rabin(long long n);


/*
 * This function generates a random number which belong to the given range.
 * Parameters:
 *		@l and @r: The left and right edge of range.
 * Returns:
 *		Returns the random number.
 */
long long random(long long l, long long r);


/*
 * This function computes the power result mod n.
 * Parameters:
 *		@x: The parameter of base number.
 *		@i: The parameter of index number.
 *		@n: The parameter of mod number.
 * Returns:
 *		Return the result of x ^ i % n
 */
long long power_mod(long long x, long long i, long long n);


/*
 * This function computes the multiply result mod n.
 * Parameters:
 *		@a & @b: The numbers to multiply.
 * 		@n: The parameter of mod number;
 * Returns:
 *		Return the result of a * b % n.
 */
long long multiply_mod(long long a, long long b, long long n);


/*
 * The main program.
 */
int main(void) {
	srand(time(NULL));
	int t;
	scanf("%d", &t);
	for(int i = 0; i < t; i++) {
		long long n;
		scanf("%lld", &n);
		
		int result = miller_rabin(n);
		if(result == 1) {
			printf("Yes\n");
		} else {
			printf("No\n");
		}
	}
	
	return 0;
}


/*
 * This function uses Miller-Rabin algorithm to check if the 
 * number is prime.
 * Parameters:
 *		@n: The number to check.
 * Returns:
 *		If the number is prime number returns 1, or returns 0.
 */
int miller_rabin(long long n) {
	if(n == 1) {
		return 0;
	} else if(n == 2) {
		return 1;
	} else if(n % 2 == 0) {
		return 0;
	}
	
	long long u = n - 1;
	while(u % 2 == 0) {
		u /= 2;
	}
	long long u0 = u;
	
	for(int s = 0; s < 10; s++) {
		long long a = random(2, n - 1);
		long long x = power_mod(a, u, n);
		u = u0;
		while(u < n) {
			long long y = multiply_mod(x, x, n);
			if(y == 1 && x != 1 && x != n - 1) {
				return 0;
			}
		  	u *= 2;
		  	x = y;
		}
		
		if(x != 1) {
			return 0;
		}
	}
	return 1;
}


/*
 * This function generates a random number which belong to the given range.
 * Parameters:
 *		@l and @r: The left and right edge of range.
 * Returns:
 *		Returns the random number.
 */
long long random(long long l, long long r) {
	long long x = rand() % (r - l + 1);
 	x += l;
	return x;
}


/*
 * This function computes the power result mod n.
 * Parameters:
 *		@x: The parameter of base number.
 *		@index: The parameter of index number.
 *		@n: The parameter of mod number.
 * Returns:
 *		Return the result of x ^ i % n
 */
long long power_mod(long long x, long long index, long long n) {
	long long list[64];
	list[0] = x;
	
	for(int i = 1; i < 64; i++) {
		list[i] = multiply_mod(list[i - 1], list[i - 1], n);
	}
	
	long long result = 1;
	
	int p = 0;
	while(index > 0) {
		int t = index % 2;
		if(t == 1) {
			result = multiply_mod(result, list[p], n);
		}	
		index /= 2;
		p++;	
	}
	
	return result;
}


/*
 * This function computes the multiply result mod n.
 * Parameters:
 *		@a & @b: The numbers to multiply.
 * 		@n: The parameter of mod number;
 * Returns:
 *		Return the result of a * b % n.
 */
long long multiply_mod(long long a, long long b, long long n) {
	long long list[64];
	list[0] = a;
	
	for(int i = 1; i < 64; i++) {
		list[i] = list[i - 1] * 2;
		list[i] %= n;
	}
	
	long long result = 0;
	
	int p = 0;
	while(b > 0) {
		int x = b % 2;
		if(x == 1) {
			result += list[p];
			result %= n; 
		}	
		b /= 2;
		p++;	
	}
	
	return result;
}
 
  

你可能感兴趣的:(ACM)