1. 程式人生 > >判斷一個數是不是素數的最快的方法(程式碼可以執行,Miller_Rabin + 新的)

判斷一個數是不是素數的最快的方法(程式碼可以執行,Miller_Rabin + 新的)

1.自己寫的(某種演算法思想的改進),很快! (只是判斷一個素數,如果資料量比較大,那麼會超時)

#include <cstdio>
#include <cmath>
#include <cstring>
int visit[100000000];
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF){
		int N  =(int) sqrt(1.0*n);
    	if(n%2==0)
   			printf("NO\n");
  		else if(n == 3||n==5 ||n==7 ||n==11 ||n==13||n==17||n==19)
	   		printf("YES\n");
	    else{
	   		memset(visit,0,sizeof(visit));
	   		for(int j=2;j<=N;j++)
	  			visit[j] = j;
	   		int flag = 2;
		  	bool FF = true;
			bool F2 = true;
		   	for(int i=3; i<=N; )
			{
			  	if(n%i==0){
			   		FF = false;
			   		break;
	   			}
			        else{       // 如果除以 i 不行,那麼 除以i 的倍數也不能夠整除。
					//printf(" i = %d , 剩餘的等待檢測的數是  ",i);
			   		for(int j=i;j<=N;j+=i)
			   			visit[j] = 0;   //這些數,不能再作為 因子嘗試了。
			   		for(int j=i;j<=N;j++){
				   		if(visit[j]!=0){    //找到第一個不是 i 倍數的數
				   			i=j;
				   			break;
				   		}
						if(j == N) F2 = false;
					}
			   	}
			   	if(!F2)
			   		break;
				/*for(int j=3;j<=N;j++)
					printf("%d ",visit[j]);
				printf("\n"); */
			}
		   	if(!FF) printf("NO\n");
		        else printf("YES\n");
		}
	}
	return 0;
}


2.Miller_Rabin 隨機數判斷素數的方法(應該是目前最快,把費馬小定理反過來用,S越大,判斷結果越正確,但是,基本上不會判錯)

剛學習的,感覺很好用! (無論資料量大小,都適應,很好用的!!!)

參考學習:vongang.kuangbin

/*
費馬小定理:設p 是素數,a與p互素,則 a^(p-1) ≡ 1 (mod p).
這個定理反過來用,p 幾乎一定是素數(也是成立的)
*/
#include <cstdio>
#include <cstdlib>

const int S = 2;    //進行S次測試。 其實,2次 基本可以了。 多次更準確

/*
計算 (a*b)%c 的結果
注意:a*b%n = (a%n * b%n) %n.
      (a+b)%n = (a%n + b%n) %n
*/
long long mult_mod(long long a,long long b,long long c)
{
    a = a%c;
    b = b%c;
    long long ret = 0;
    while(b){
        if(b&1){    //因為對於二進位制而言,每次都是看最後一個一
            ret += a;
            ret %= c;
        }
        a = a<<1;   //因為b每次最多是1,所以把 此時 2^( a左移的次數)看作 真實算式中b的大小
        if(a>=c) a%=c;
        b = b >>1;  // b 對 2 取整
    }
    return ret;
}

/*
解決 x^n %mod 的結果
把 x 和 n 利用二進位制進行展開,很容易求得。
*/
long long pow_mod(long long x,long long n,long long mod)
{
    if(n==1) return x%mod;
    x %= mod;
    long long tmp = x;
    long long ret = 1;
    while(n){
        if(n&1)
            ret = mult_mod(ret,tmp,mod); //ret = (ret *tmp) %mod
        tmp = mult_mod(tmp,tmp,mod); //tmp的每次取值是 x^1,x^2,x^4,x^8
        n = n>>1;
    }
    return ret;
}
/*
二次探測定理:
  如果p是奇素數,則 x^2 ≡ 1(mod p)的解為 x = 1 || x = p - 1(mod p);
*/

//t 是移位數。
long long check(long long a,long long x,long long n,long long t)
{
    long long ret = pow_mod(a,x,n);//a^x %n 快速冪取模
    long long last = ret;                           //(並且 a 與 n 互素,呼叫之前已經保證了 )
    for(int i=1;i<=t;i++){  //移位減掉的量補上 ! 全部補完之後是 a^(n-1) % n ,看結果是否是 1
        ret = mult_mod(ret,ret,n); //(ret*ret) % n
        if(ret ==1 && last!=1 && last!=n-1) //二次探測定理
            return true;//不是素數
        last = ret;
    }
    if(ret!=1)  //不是素數
        return true;
    return false;
}
/*
  Miller-Rabin測試:不斷選取不超過n-1的基b(s次),
計算是否每次都有bn-1 ≡ 1(mod n),
若每次都成立則n是素數,否則為合數。
*/
bool Miller_Rabin(long long n)
{
    if(n<2) return false;
    if(n==2) return true;
    if((n&1) ==0) return false;//偶數排除
    //因為 判斷公式是 x^(n-1) % n = 1 .
    //a^(n-1) ≡ 1(mod n)
    long long x = n - 1; // 求x^u % n
    long long t = 0;
    //如果 x 為偶數則x右移,用 t 記錄移位數
    while((x&1) ==0){
        x = x >> 1; //右移是相除 ,結果越來越小
        t++;
    }
    for(int i=0;i<S;i++){
            //我覺得這個地方換成 n-2 比較好
        long long a = rand()%(n-1)+1;  //在 [1,n) 中 取隨機數
        if(a%n ==0) continue;   //自加!!!!!!!!!!!!!!!!!!!!!!!
        if( check (a,x,n,t) )
            return false;
    }
    return true;
}

int main()
{
    for(long long value = 1000000;value<=1000500;value++){
        if(Miller_Rabin(value))
            printf("%lld 是素數\n",value);
        else
            printf("NO\n");
    }
    return 0;
}