码迷,mamicode.com
首页 > 其他好文 > 详细

ExpertC题目 列出所有不大于N的素数

时间:2015-07-15 10:44:21      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:

 列出所有不大于N的素数

//-------------------------------------------------------

3.(1) 以sqrt(N)来逐个判断,优化可以只对1~N的奇数进行判断,分母为3~sqrt(N)的奇数,可以减少一半运行时间;

   (2)有更复杂的方法涉及到质数算法、厄拉多塞篩法

厄拉多塞篩法,小时候接触过。

西元前250年,希臘數學家、亞歷山大圖書館館長厄拉多塞(Eeatosthese)想到了一個非常美妙的質數篩法,減少了逐一檢查每個數的的步驟,可以比較簡單的從一大堆數字之中,篩選出質數來,這方法被稱作厄拉多塞篩法(Sieve of Eeatosthese)。
以上表為例,2是質數,以2為篩子,留下2並刪去2的倍數;2之後未被刪去的第一個數是3,它是質數。以3為篩子,留下3並刪去3的倍數;3之後未被刪去的第一個數是5,它是質數。以5為篩子,留下5並刪去5的倍數;5之後未被刪去的第一個數是7,它是質數。以7為篩子,留下7並刪去7的倍數;7之後未被刪去的第一個數是11,它是質數。到此就算完成尋找所有介於1和50之間的質數,因為50的平方根大於7而且小於8,現在表格所剩的數字都是質數。

 

问题描述:
       给你一个1~2^31之间的数,包括1和2^31,是素数输出YES,否则NO

解题过程:
(1)一般的判断素数的算法,比如判断N是能被从2到sqrt(N)范围内的整数整除,如果是,则N是合
           数,否则N是素数,显然这肯定会超时。如果用找质数算法(Sieve of Eratosthenes筛法),那
           么空间开销太大,这两种算法都不能达到要求.

(2)这里我们用费马小定理+二次探测定理,但是判断小数用的是Sieve of Eratosthenes筛选法
(3)最大数是2^31次,所以首先至少用unsigned int类型
定理描述:
1)Sieve of Eratosthenes筛法
由于一个合数总是可以分解成若干个质数的乘积,那么如果把质数(最初只知道2是质数)的倍数都去掉,那么剩下的就是质数了。例如要查找100以内的质数,首先2是质数,把2的倍数去掉;此时3没有被去掉,可认为是质数,所以把3的倍数去掉;再到5,再到7,7之后呢,因为8,9,10刚才都被去掉了,而100以内的任意合数肯定都有一个因子小于10(100的开方),所以,去掉,2,3,5,7的倍数后剩下的都是质数了。
2)费马小定理+二次探测
费马小定理: 如果P是一个素数,且0<a<p,则a^(p-1)≡1(mod p)。 
例如,67是一个素数,则2^66mod 67=1。 利用费马小定理,对于给定的整数n,可以设计一个素数判定算法。
通过计算d=2^(n-1)mod n来判定整数n的素性。当d不等于1时,n肯定不是素数;当d等于1时, n则很可能是素数。但也存在合数n使得2^(n-1)≡1(mod n)。例如,满足此条件的最小合数是n=341。为了提高测试的准确性,我们可以随机地选取整数1<a<n-1,然后用条件a^(n-1)≡1(mod n)来判定整数n的素性。例如对于n=341,取a=3时,有3^340≡56(mod 341)。故可判定n不是素数。
    费马小定理毕竟只是素数判定的一个必要条件。满足费马小定理条件的整数n未必全是素数。有些合数也满足费马小定理的条件。这些和数被称做Carmichael数,前3个Carmichael数是561,1105,1729。Carmichael数是非常少的。在1~100000000范围内的整数中,只有255个Carmichael数。利用下面的二次探测定理可以对上面的素数判定算法作进一步改进,以避免将Carmichael数当作素数。 
    
二次探测定理 如果p是一个素数,且0<x<p,则方程x^2≡1(mod p)的解为x=1,p-1。
(3)模取幂
关于快速求(a^p) mod n
令p=CnCn-1...C1C0,其中Cj(0=<j<=n)等于0或1,则a^p=a^[C0*(2^0)] * a^[C1*(2^1)]*...*a^[Cn*(2^n)],另外如果a%b = k , 则a = nb+k,则a^2%b = (nb+k)%b = k^2%b = (a%b)*(a%b)%b ,由递归可求出a^4%b ,a^8%b,......。又因为若a = b*c,那么a%n = [(b%n)*(c%n)]%n。这样,(a^p)% n等于上面等式右边各项%n
程序实现:
#include <stdio.h> 
#include <math.h> 
#include <stdlib.h>
//求(a^b) mod n
long long judge(long long a, long long b, long long n){ 
 long long d=1 , t=a;    
 while (b>0) { 
  if (t==1) return d ;
  if (b%2==1) d=d*t%n; 
    b/=2; 
    t=t*t%n; 
 } 
 return d; 
} 
 
bool nprime[10001] = {1,1};
void psieve(int n){
 int t = (int)sqrt(n),     tmp ;
 for ( int i = 2; i <= t; i++ ){
  if ( !nprime[i] ) 
   for( int j=i; (tmp=j*i) <=n; j++ ) nprime[tmp] = true ; 
 }
}
 
int main(){    
 unsigned int n ;
 psieve(10000) ; 
 srand(100) ;
 while( scanf("%u", &n) != EOF ){ 
  if ( n <= 10000 ) printf("%s\n", nprime[n] ? "NO" : "YES") ;
  else if ( n%2 == 0 || n%3 == 0 ) puts("NO") ;
  else {
   int i, a ;
   for (i=1; i<=5; i++){ 
      a = 2 + rand()%(n-2) ; 
    if ( judge(a, n-1, n) != 1 ) break ;
   } 
   if( i == 6 ) puts("YES") ;
   else puts("NO") ;
  }
 } 
 return 0; 
}
疑点:
(1)其实本题并没有用二次探测,但还是AC了,可能数据里没有Carmichael数
(2)关于如何实现二次探测,如果随机抽取几个1到p-1之间的数x来测试x^2%p==1,那么还是一个概率问题,如果对于sqrt(p)到p-1都做一次测试,那显然那太费时间了,关于这个还没有好的想法
(3)在程序中如果只用费马小定理判断那三个Carmichael数,即561,1105,1729,那么输出都是NO,说明这三个数都不满足费马小定理的条件,因为同做这题的人很多人遇到了这个问题,要不小定理有问题,要不我的模取幂代码写得有问题

 

ExpertC题目 列出所有不大于N的素数

标签:

原文地址:http://www.cnblogs.com/buaavanbo/p/4647541.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!