2018 ACM-ICPC 中国大学生程序设计竞赛线上赛 B Goldbach (素数测试,随机化算法)

题面:
  • 5.67%
  • 1000ms
  • 65536K

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

    题面描述:哥德巴赫猜想。即给你一个偶数,让你分解成两个质数。
    题面分析:拿到题目,一看到数据范围就懵逼了(妈耶1e18的数据范围!!(>人<;))最开始的时候就不断往素数筛的方向去思考,但是总感觉素数筛的话很可能会TLE。好在队友经验丰富,知道有一种叫做米勒罗宾的素性测试(不懂什么是米勒罗宾素性测试的可以参考一下这篇博文传送门
    简单来说米勒罗宾算法就是费马小定理的推广,可以在相对快速的时间内判断一个数是否为素数(这里运用了随机化算法的思想)如果知道这个算法的话我们大可从3开始枚举,不断判断i和n-i是否为素数即可。

    因为根据哥德巴赫猜想可以知道,一个偶数必可以分解成两个质数之和,因此,我们从3开始枚举,最极端的情况下只需要枚举到n/2(如果枚举超过n的一半了还不能判定的话,一是米勒罗宾写丑了,二是哥德巴赫猜想是错的(逃~))即可。在这题下,极端情况大约在1e9(勉勉强强卡过,谢出题人不杀之恩/(ㄒoㄒ)/~~)。
    贴出AC代码

    

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int S=10;
ll mult_mod(ll a,ll b,ll c){//快速乘
    a%=c;
    b%=c;
    ll ret=0;
    ll tmp=a;
    while(b){
        if(b&1){
            ret+=tmp;
            if(ret>c) ret-=c;//直接取模会慢很多
        }
        tmp<<=1;
        if(tmp>c) tmp-=c;
        b>>=1;
    }
    return ret;
}
//计算ret=(a^n)%mod;
ll pow_mod(ll a,ll n,ll mod){
    ll ret=1;
    ll tmp=a%mod;
    while(n){
        if(n&1) ret=mult_mod(ret,tmp,mod);
        tmp=mult_mod(tmp,tmp,mod);
        n>>=1;
    }
    return ret;
}
bool check(ll a,ll n,ll x,ll t){
    ll ret=pow_mod(a,x,n);
    ll last=ret;
    for(int i=1;i<=t;i++){
        ret=mult_mod(ret,ret,n);
        if(ret==1&&last!=1&&last!=n-1) return true;
        last=ret;
    }
    if(ret!=1) return true;
    return false;
}
bool Miller_Rabin(ll n){
    if(n<2) return false;
    if(n==2) return true;
    if((n&1)==0) return false;
    ll x=n-1;
    ll t=0;
    while((x&1)==1){
        x>>=1;
        t++;
    }
    for(int i=0;i<S;i++){
        ll a=rand()%(n-1)+1;
        if(check(a,n,x,t)) return false;
    }
    return true;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        ll n;
        cin>>n;
        if(n==4||n==6) cout<<n/2<<" "<<n/2<<endl;
        else{
            for(ll i=3;i<=n;i+=2){
                if(!Miller_Rabin(i)) continue;
                if(Miller_Rabin(i)&&Miller_Rabin(n-i)){
                    cout<<i<<" "<<n-i<<endl;
                    break;
                }
            }
        }
    }
    return 0;
}

    打完比赛问师兄,你们打米勒罗宾怎么这么快!!?
    答曰:我们用的是java+随机化算法(震惊(○´・д・)ノ)

    嗯......貌似java里面的BigInteger是有封装米勒罗宾素数判断的方法的。
    直接是用这个方法,然后随机化不断求解就可以了(但是正因为这个方法是随机化的,就有可能人品极差,导致交上去wrong answer的╮(╯▽╰)╭)ps(米勒罗宾算法本身好像就可能晒出伪素数的,这个要注意!!!)

    嗯。。。学艺不精,还得继续努力!
    附上java代码:
    
import java.util.*;
import java.math.*;
public class Main {
	public static BigInteger getrand(long l,long r) {
		Random rand=new Random();
		return BigInteger.valueOf(Math.abs(rand.nextLong())%(r-l+1)+l);
	}
	public static void main(String[] args) {
		Scanner sca=new Scanner(System.in);
		int t;
		t=sca.nextInt();
		while(t-->0) {
			BigInteger L,R;
			BigInteger n=sca.nextBigInteger();
			while(true) {
				L=getrand(1,n.longValue());
				R=n.subtract(L);
				if(L.isProbablePrime(100)&&R.isProbablePrime(100)) {
					break;
				}
			}
			System.out.println(L+" "+R);
		}
	}
}

posted @ 2018-04-22 21:13  ChenJr  阅读(274)  评论(0编辑  收藏  举报