A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

今天研究了快一下午,终于由刚开始筛不出来到100秒到十秒,再优化算法到5秒再到现在只需要3秒,,


可是一些玩C的终极大神半秒就能搞定。。好郁闷,,,
下面是我的实现代码:

  1. package cn.wangzhe.test;


  2. import java.util.*;

  3. public class  GetPrimeNumber
  4. {
  5.     public static void main(String[] args){
  6.             final int MAX=100000000;
  7.             long start = System.currentTimeMillis();

  8.             int[] arr=getPrimeNumArray(MAX);

  9.             long end = System.currentTimeMillis();
  10.             System.out.println("数组筛法:"+(end-start)+"ms");
  11.            
  12.             //System.out.println(Arrays.toString(arr));
  13.            
  14.            
  15.             start = System.currentTimeMillis();
  16.            
  17.             arr=getPrimeNumArray2(MAX);
  18.            
  19.             end = System.currentTimeMillis();
  20.             System.out.println("数组筛法2:"+(end-start)+"ms");
  21.            
  22.            
  23.             /*for(int i=0;i<list.size();i++){
  24.                    
  25.             }
  26.             System.out.println(true);*/
  27.            
  28.     }
  29.     /*这里我们来讨论一下用“筛法”来解决这个问题。
  30.    先来举个简单的例子来介绍一下“筛法”,求2~20的质数,它的做法是先把2~20这些数一字排开:
  31.    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  32.    先取出数组中最小的数,是2,则判断2是质数,把后面2的倍数全部删掉。
  33.    2 | 3 5 7 9 11 13 15 17 19
  34.    接下来的最小数是3,取出,再删掉3的倍数
  35.    2 3 | 5 7 11 13 17 19
  36.    一直这样下去,直到结束。剩下的数都是素数。
  37.    筛法的原理是:
  38.    1.数字2是素数。
  39.    2.在数字K前,每找到一个素数,都会删除它的倍数,即以它为因子的整数。
  40.         如果k未被删除,就表示2->k-1都不是k的因子,那k自然就是素数了。
  41.    (1)除余法那篇文章里也介绍了,要找出一个数的因子,其实不需要检查2->k,
  42. 只要从2->sqrt(k),就可以了。所有,我们筛法里,其实只要筛到sqrt(n)就已经找出所有的素数了,其中n为要搜索的范围。
  43.    (2)另外,我们不难发现,每找到一个素数k,就一次删除2k, 3k, 4k,..., ik,不免还是有些浪费,
  44. 因为2k已经在找到素数2的时候删除过了,3k已经在找到素数3的时候删除了。因此,当i<k时,
  45. 都已经被前面的素数删除过了,只有那些最小的质因子是k的那些数还未被删除过,所有,就可以直接从k*k开始删除。
  46.    (3)再有,所有的素数中,除了2以外,其他的都是奇数,那么,当i时奇数的时候
  47. ,ik就是奇数,此时k*k+ik就是个偶数,偶数已经被2删除了,
  48. 所有我们就可以以2k为单位删除步长,依次删除k*k, k*k+2k, k*k+4k, ...。
  49.   */
  50.     //数组筛法
  51.     public static int[] getPrimeNumArray2(int max) {
  52.             boolean[] flag=new boolean[max];
  53.             int gab = 4;
  54.             flag[4]=true;
  55.             for(int i=5;i<=Math.sqrt(max);i += gab ^= 6)
  56.                     for(int j=i*i;j<max;j+=2*i)
  57.                             flag[j]=true;
  58.            
  59.             int[] primeArray=new int[16];
  60.             int len=0;
  61.             for(int i=2;i<flag.length;i++){
  62.                     if(!flag[i]){
  63.                             primeArray[len++]=i;
  64.                             primeArray = sureArraylen(primeArray, len);
  65.                     }
  66.             }
  67.             int[] result=new int[len];
  68.             System.arraycopy(primeArray, 0, result, 0, len);
  69.             return result;
  70.     }
  71.     public static int[] getPrimeNumArray(int max) {
  72.             boolean[] flag=new boolean[max];
  73.            
  74.             for(int j=2*2;j<max;j+=2)
  75.                     flag[j]=true;
  76.             for(int i=3;i<=Math.sqrt(max);i++)
  77.                     for(int j=i*i;j<max;j+=2*i)
  78.                             flag[j]=true;
  79.             //return null;
  80.            
  81.             int[] primeArray=new int[16];
  82.             int len=0;
  83.             for(int i=2;i<flag.length;i++){
  84.                     if(!flag[i]){
  85.                             primeArray[len++]=i;
  86.                             primeArray = sureArraylen(primeArray, len);
  87.                     }
  88.             }
  89.             int[] result=new int[len];
  90.             System.arraycopy(primeArray, 0, result, 0, len);
  91.             return result;
  92.     }

  93.         public static int[] sureArraylen(int[] primeArray, int len) {
  94.                 if(len==primeArray.length){
  95.                         int[] temp=new int[primeArray.length*2+2];
  96.                         System.arraycopy(primeArray, 0, temp, 0, len);
  97.                         primeArray=temp;
  98.                 }
  99.                 return primeArray;
  100.         }
  101.            
  102.         /*
  103.          * (5)由(2)可知,如果每找到一个素数k,能依次只删除以k为最小素数因子的数,
  104.          * 那么每个数字就都只被删除一次,那这个筛法就能达到线性的O(n)效率了。
  105.          * 比如数字600 = 2*2*3*5*11,其中2是它的最小素数因子。那这个数就被2删除了。
  106.          * 3、5、11虽然都是它的因子,但不做删除它的操作。要实现这种策略,那每找到一个素数k,
  107.          * 那从k开始,一次后面未被删除的数字来与k相乘,删除它们的积。比如要筛出2~60之间的素数:
  108.    1.先列出所有的数。
  109.    2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 50 51 52 53 54 55 56 57 58 59 60
  110.    2.选出序列中的第一个数,即2,判断它是素数,然后从2开始,依次与剩下的未被删除的数相乘,
  111. 删除它们的积。即2*2=4, 2*3=6,2*4=8...。
  112.    2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 50 51 52 53 54 55 56 57 58 59 60
  113.    02 | 03 05 07 09 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59
  114.    3.去掉2后,再选出序列中第一个数,即3,判断它是素数,然后从3开始,依次与剩下的数相乘,即3*3=9,3*5=15,3*7=21...
  115.    02 | 03 05 07 09 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59
  116.    02 03 | 05 07 11 13 15 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59
  117.    4.去掉3后,选出最小的数5,为素数,依次删除5*5=25,5*7=35,5*11=55,...
  118.    02 03 | 05 07 11 13 15 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59
  119.    02 03 05 | 07 11 13 15 17 19 23 29 31 37 41 43 47 49 53 59
  120.    5.去掉5后,选出最小的数7,为素数,删除7*7=49,...
  121.    02 03 05 | 07 11 13 15 17 19 23 29 31 37 41 43 47 49 53 59
  122.    02 03 05 | 07 11 13 15 17 19 23 29 31 37 41 43 47 53 59
  123.    6.去掉7后,第一个数11的平方121大于60,所以结束。剩下的数字全为素数。
  124.    02 03 05 07 11 13 15 17 19 23 29 31 37 41 43 47 53 59 |
  125.    上面的操作效率很高,但在计算机中模拟的时候却又很大的障碍:
  126.    首先,计算机内存是一维的空间,很多时候我们不能随心所欲,要实现上面的算法,
  127. 要求这个数据结构既能很高效地查找某个特定的值,又能不费太大代价对序列中的元素进行删除。
  128. 高效地查找,用数组是最合适的了,能在O(1)的时间内对内存进行读写,但要删除序列中一个元素却要O(n);
  129. 单链表可以用O(1)的时间做删除操作,当然要查找就只能是O(n)了。所以这个数据结构很难找。
  130.    其次,筛法的一个缺点就是空间浪费太大,典型的以空间换时间。如果我们对数组进行压缩,
  131. 比如初始时就排除了所有偶数,数组0对应数字1,1对应3,...。这样又会因为多了一道计算数字下标的工序而浪费时间。
  132. 这又是一个矛盾的问题。
  133.    也许我们可以试试折中的办法:数据结构综合数组和链表2种,数组用来做映射记录,链表来记录剩下的还未被删除的数据,
  134. 而且开始也不必急着把链表里的节点释放掉,只要在数组里做个标记就可以了。下次遍历到这个数字时才删除。
  135. 这样为了删除,可以算只遍历了一次链表,不过频繁地使用free()函数,也许又会减低效率。总之,我们所做的,
  136. 依然是用空间来换时间,记录更多的信息,方便下次使用,减少再次生成信息所消耗的时间。
  137.          */
  138.         /*public static int[] getPrimeNumArray3(int max) {
  139.             boolean[] flag=new boolean[max];
  140.             for(int i=2;i<max;i++){
  141.                     for(int j=i;!flag[j]&&i*j<max;j++)
  142.                             flag[i*j]=true;
  143.             }
  144.            
  145.             int[] primeArray=new int[16];
  146.             int len=0;
  147.             for(int i=2;i<flag.length;i++){
  148.                     if(!flag[i]){
  149.                             primeArray[len++]=i;
  150.                             primeArray = sureArraylen(primeArray, len);
  151.                     }
  152.             }
  153.             int[] result=new int[len];
  154.             System.arraycopy(primeArray, 0, result, 0, len);
  155.             return result;
  156.     }*/
  157.        
  158.         public static List<Integer> getPrimeNumList(int max) {//集合筛法
  159.                 List<Integer> src=new LinkedList<Integer>();//创建一个集合进行筛
  160.                 List<Integer> result=new LinkedList<Integer>();//保存筛出的素数
  161.                
  162.                 //初始化集合
  163.                 src.add(2);
  164.                 for(int i=3;i<=max;i+=2)
  165.                         src.add(i);
  166.                
  167.                 Integer a=null;//初始化除数
  168.                 while(src.size()>0){//如果第一个集合内还有值就继续筛
  169.                         a=src.remove(0);//删除第一个集合中最小的数,这个数一定是素数
  170.                        
  171.                         for(int i=0;i<src.size();i++)//删除集合中这个数的倍数
  172.                                 if(src.get(i)%a==0)
  173.                                         src.remove(i);
  174.                        
  175.                         result.add(a);//将这个素数加入集合2
  176.                 }
  177.                 return result;
  178.         }

  179. }


复制代码

思路来源:http://blog.csdn.net/controlfate/article/details/6758202

6 个回复

倒序浏览
看不懂。。
回复 使用道具 举报

这个需要大神中的大神才能看懂并优化
回复 使用道具 举报
as604049322 发表于 2015-5-18 18:15
这个需要大神中的大神才能看懂并优化

确实如此
回复 使用道具 举报
我擦 直接懵了:L
回复 使用道具 举报
as604049322 来自手机 金牌黑马 2015-5-19 04:33:44
地板
我擦,这么屌的算法,居然没人看。。。
回复 使用道具 举报
灯火通明 来自手机 中级黑马 2015-5-19 06:45:59
7#
还不错。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马