求素数的第N种境界的实现(需求1)

来源:互联网 发布:韩国高考难度知乎 编辑:程序博客网 时间:2024/05/24 01:06

出处:program_think大神讲的《求质数算法的N种境界 (N > 10)》 http://blog.csdn.net/program_think/article/details/7032600

需求1:/*请实现一个函数,对于给定的整型参数 N,该函数能够把自然数中,小于 N 的质数,从小到大打印出来。
 比如,当 N = 10,则打印出
2 3 5 7*/

"◇境界1

  在试除法中,最最土的做法,就是:
  假设要判断 x 是否为质数,就从 2 一直尝试到 x-1。这种做法,其效率应该是最差的。如果这道题目有10分,按照这种方式做出的代码,即便正确无误,俺也只给1分。"

实现代码:

#include <stdio.h>
#include <math.h>

int prime(int max)
{
 int i;
 if(max<=1) return 0;
 if(max==2) return 2;
 for(i=2;i<max-1;i++)
  if(max%i==0) return 0;
  return max;
}
int main()
{
 int i,N,k;
 scanf("%d",&N);
 for(i=2;i<N;i++)
  if((k=prime(i))!=0) printf("%d\t",k);
 return 0;
}

 

◇境界2

  稍微聪明一点点的程序猿,会想:x 如果有(除了自身以外的)质因数,那肯定会小于等于 x/2,所以捏,他们就从 2 一直尝试到 x/2 即可。
  这一下子就少了一半的工作量哦,但依然是很笨的办法。打分的话,即便代码正确也只有2分”

 

实现代码:

#include <stdio.h>
#include <math.h>
int prime(int max)
{
 int i;
 if(max<=1) return 0;
 if(max==2) return 2;
 for(i=2;i<max/2;i++)
  if(max%i==0) return 0;
  return max;
}
int main()
{
 int i,N,k;
 scanf("%d",&N);
 for(i=2;i<N;i++)
  if((k=prime(i))!=0) printf("%d\t",k);
 return 0;
}

"◇境界4

  比前3种程序猿更聪明的,就会发现:其实只要从 2 一直尝试到√x,就可以了。估计有些网友想不通了,为什么只要到√x 即可?
  简单解释一下:因数都是成对出现的。比如,100的因数有:1和100,2和50,4和25,5和20,10和10。看出来没有?成对的因数,其中一个必然小于等于100的开平方,另一个大于等于100的开平方。至于严密的数学证明,用小学数学知识就可以搞定,俺就不啰嗦了"

实现代码:

#include <stdio.h>
#include <math.h>
int prime(int max)
{
 int i;
 int k=floor(sqrt(max));
 if(max<=1) return 0;
 if(max==2) return 2;
 for(i=2;i<=k;i++)
  if(max%i==0) return 0;
  return max;
}
int main()
{
 int i,N,k;
 scanf("%d",&N);
 for(i=2;i<N;i++)
  if((k=prime(i))!=0) printf("%d\t",k);
 return 0;
}

◇境界5

  那么,如果先尝试2,然后再针对 3 到√x 的所有奇数进行试除,是不是就足够优了捏?答案显然是否定的嘛?写到这里,才刚开始热身哦。
  一些更加聪明的程序猿,会发现一个问题:尝试从 3 到√x 的所有奇数,还是有些浪费。比如要判断101是否质数,101的根号取整后是10,那么,按照境界4,需要尝试的奇数分别是:3,5,7,9。但是你发现没有,对9的尝试是多余的。不能被3整除,必然不能被9整除......顺着这个思路走下去,这些程序猿就会发现:其实,只要尝试小于√x  的质数即可。而这些质数,恰好前面已经算出来了(是不是觉得很妙?)。
  所以,处于这种境界的程序猿,会把已经算出的质数,先保存起来,然后用于后续的试除,效率就大大提高了。
  顺便说一下,这就是算法理论中经常提到的:以空间换时间

实现代码:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main()

 int max,j,len,m,k;
 int *i;
 scanf("%d",&max);
 if(max<=2) return 0;
 i=(int *)malloc(sizeof(int)*max); 
 i[0]=2;
 for(j=3,len=1;j<max;j++)
 { 
  k=(int)floor(sqrt(j));
  for(m=0;m<len;m++)
  {
   if(j%i[m]==0) break;
   if(i[m]>=k)
   {
    i[len]=j;
    len++;
    break; 
   }
  }
  
 }
 
 for(j=0;j<len;j++)
 {
  printf("%d\t",i[j]);
 }
 return 0;
}

*********************************************************

★筛法

*********************************************************

 

"境界1
  照例先说说最土的搞法——直接构造一个整型的容器。在筛的过程中把发现的合数删除掉,最后容器中就只剩下质数了。
  为啥说这种搞法最土捏?
  首先,整型的容器,浪费内存空间。比方说,你用的是32位的C/C++或者是Java,那么每个 int 都至少用掉4个字节的内存。当 N 很大时,内存开销就成问题了。
  其次,当 N 很大时,频繁地对一个大的容器进行删除操作可能会导致频繁的内存分配和释放(具体取决于容器的实现方式);而频繁的内存分配/释放,会导致明显的CPU占用并可能造成内存碎片。"

 

实现代码:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
int main()

 clock_t start,end;
 int max,n,k;
 int *i;
 scanf("%d",&max);
 start=clock();
 i=(int *)malloc(sizeof(int)*max);
 for(n=0;n<max;n++)
  i[n]=1;
 i[0]=0;
 i[1]=0;
 for(n=0;n<max;n++)
 {
  if(i[n]==1)
  {
   printf("%d\t",n);
   for(k=2*n;k<max;k+=n)
   {
    i[k]=0;
   }
  }
 }
 end=clock();
 printf("/n用时:%d",end-start);
 return 0;
}

 

********************************************************************

发现耗时没有明显提升,因为printf占用了很大一部分时间

减去printf后发现试除法境界5和筛法境界1耗时相同!

********************************************************************

原创粉丝点击