2018 ACM-ICPC 中国大学生程序设计竞赛线上赛 Goldbach(2^63 次方内的素数判断,Miller-Rabin素数检测算法)

Description:

Goldbach's conjecture is one of the oldest and best-known unsolved problems in number theory and all of mathematics. It states:

Every even integer greater than 2 can be expressed as the sum of two primes.

The actual verification of the Goldbach conjecture shows that even numbers below at least 1e14 can be expressed as a sum of two prime numbers. 

Many times, there are more than one way to represent even numbers as two prime numbers. 

For example, 18=5+13=7+11, 64=3+61=5+59=11+53=17+47=23+41, etc.

Now this problem is asking you to divide a postive even integer n (2<n<2^63) into two prime numbers.

Although a certain scope of the problem has not been strictly proved the correctness of Goldbach's conjecture, we still hope that you can solve it. 

If you find that an even number of Goldbach conjectures are not true, then this question will be wrong, but we would like to congratulate you on solving this math problem that has plagued humanity for hundreds of years.

Input:

The first line of input is a T means the number of the cases.

Next T lines, each line is a postive even integer n (2<n<2^63).

Output:

The output is also T lines, each line is two number we asked for.

T is about 100.

本题答案不唯一,符合要求的答案均正确

样例输入

1
8

样例输出

3 5

题目来源

2018 ACM-ICPC 中国大学生程序设计竞赛线上赛

题意:给你一个2^63次方内的合数(偶数),让 你拆分为 两个素数的和;由于数的范围比较大,所以一般的筛法都不行,不管是 埃氏筛选素数法,还是 欧拉筛都需要标记;所以在这介绍一种 全新的 判断大数是不是素数的算法:MIller_rabin 素数检测算法;

MIller_rabin素数检测算法中 包含了 费马小定理 和 二次探测定理,运用经 费马小定理和二次探测定理 的逆否定理多次排除后,若能留下来就说明一定是素数

下面介绍几个结论:

1. 费马小定理:假如p是质数,a是整数,且a、p互质,那么a的(p-1)次方除以p的余数恒等于1,即:a^(p-1)≡1(mod p).

但是反过来却不一定成立,就是说,如果a、p互质,且a^(p-1)≡1(mod p),不能推出p是质数,比如Carmichael数。

卡迈克尔数的定义是对于合数n,如果对于所有正整数b,b和n互素,都有同余式b^(n-1)≡ 1 (mod n)成立,则合数n为Carmichael数

2. 二次探测定理:如果p是一个素数,0<x<p,则方程x^2≡1(mod p)的解为x=1或x=p-1。

3. 模运算的规则:(a*b)%n=(a%n * b%n)%n

4. 快速积取模、快速幂取模

然后是算法的过程:

对于要判断的数n

1.先判断是不是2,是的话就返回true。

2.判断是不是小于2的,或合数,是的话就返回false。

3.令n-1=u*2^t,求出u,t,其中u是奇数。

4.随机取一个a,且1<a<n

/*根据费马小定理,如果a^(n-1)≡1(mod p)那么n就极有可能是素数,如果等式不成立,那肯定不是素数了

因为n-1=u*2^t,所以a^(n-1)=a^(u*2^t)=(a^u)^(2^t)。*/

5.所以我们令x=(a^u)%n

6.然后是t次循环,每次循环都让y=(x*x)%n,x=y,这样t次循环之后x=a^(u*2^t)=a^(n-1)了

7.因为循环的时候y=(x*x)%n,且x肯定是小于n的,正好可以用二次探测定理,

如果(x^2)%n==1,也就是y等于1的时候,假如n是素数,那么x==1||x==n-1,如果x!=1&&x!=n-1,那么n肯定不是素数了,返回false。

8.运行到这里的时候x=a^(n-1),根据费马小定理,x!=1的话,肯定不是素数了,返回false

9.因为Miller-Rabin得到的结果的正确率为 75%,所以要多次循环步骤4~8来提高正确率

10.循环多次之后还没返回,那么n肯定是素数了,返回true

这道题而言,要用 unsigned long long ,还有就是 中往两边枚举,竟然超时,而从1 开始枚举,不超时;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define ll  unsigned long long
 
ll mod_exp(ll a,ll b,ll n) //   a*b%n 快速积取模 
{
	ll res = 0;
	while(b)
	{
		if(b&1)
			res = (res + a)%n;
		a = (a+a)%n;
		b>>=1;
	}
	return res;
}
// 为什么不直接写快速幂,还有把 a*b 拆开写,这是为了避免数大超出范围;
 
ll mod_pow(ll a,ll b,ll n)   //a^b%n;  // 快速幂取模 
{
	ll res = 1;
	while(b)
	{
		if(b&1)
			res = mod_exp(res,a,n);
		a = mod_exp(a,a,n);
		b>>=1;
	}
	return res;
}
bool miller_rabin(ll n)
{
	if(n==2)
		return true;
	if(n<=1||!(n&1))
		return false;
	ll i,j,t,u;
	for(u = n-1,t=0;!(u&1);t++,u>>=1);  // n-1 化成 u*2^t u为奇数;
	for(i = 0;i<10;i++)   // 经多次排除后,n一定是素数了; 
	{
		ll a = rand()%(n-1)+1;
		ll x = mod_pow(a,u,n);
		for(j = 0;j<t;j++)
		{
			ll y = mod_exp(x,x,n);    // 二次探测定理 如果 p为一个素数,0<x<p, x*x%p = 1,那么一定有x==1或这x==n-1; 
			if(y==1&&x!=1&&x!=n-1)   // 在这里用到了,二次探测的逆否定理; 
				return false;
			x = y;
		//如果 0<x<p,x*x%p==1,且x!=1&&x!=n-1 ,那么p一定不是素数; 
		}
		if(x!=1) return false;    // 费马小定理 
		// 若a与n互质,n为素数,那么一定 a^(n-1)%n==1
		// 在这里隐含着a与n互质,要要是n为素数,这里的a小于n所以一定互质;
		// 这里也用到了逆否定理,若 a^(n-1)%n !=1,那么n一定不是素数;  
	} 
	return true;
}
int main()
{
	ll i,j,t,n;
	scanf("%llu",&t);
	while(t--)
	{
		scanf("%llu",&n);
		for(i = 1;i<n;i++)
		{
			if(miller_rabin(i)&&miller_rabin(n-i))
			{
				printf("%llu %llu\n",i,n-i);
				break;
			}
		}
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/obsorb_knowledge/article/details/80066391