素数指的是一个只能被1和它本身整除的数,它是一个在数论中占重要研究地位的数,是一个数学皇冠上占一个重要位置的数。
目前发现的最大的素数:18世纪发现的最大素数是2^31-1,19世纪发现的最大素数是2^127-1,20世纪末人类已知的最大素数是2^859433-1,用十进制表示,这是一个258715位的数字。
- 歌德巴赫猜想:大于2的所有偶数均是两个素数的和,大于5的所有奇数均是三个素数之和。其中第二个猜想是第一个的自然推论,因此歌德巴赫猜想又被称为1+1问题。我国数学家陈景润证明了1+2,即所有大于2的偶数都是一个素数和只有两个素数因数的合数的和。国际上称为陈氏定理。
- 孪生素数猜想:差为2的素数有无穷多对。目前知道的最大的孪生素数是1159142985×22304-1和1159142985×22304+1。
- 在n2与(n+1)2之间总有素数;n2+1这种形式的素数有无穷多个。
- 大于某个n的自然数不是完全平方数,就是一个平方数与一个素数之和。
素数求取问题:求出小于N的所有素数。
- 暴力求解:遍历[2,N]中的每一个数,判断其是否是素数。时间复杂度O(N^1.5)。
public void printPrimes(int N){ System.out.println(2); for(int i=3;i<N;i++){ if(isPrime(i)) System.out.println(i); } } boolean isPrime(int n){ int sqrt = (int)Math.pow(n,0.5) +1; for(int i=2; i<sqrt; i++){ if(n%i ==0) return false; } return true; }
- 一次筛选:使用长为N+1的数组(位图更好),第i位表示i是否为素数。从第一个素数开始,将其输出,然后把后面所有它的倍数标记,找到下一个素数继续循环,直到最后。时间复杂度O(N),空间复杂度O(N)。
public void printPrimes(int N){ byte[] primes = new byte[N+1]; for(int i=2; i<=N; i++) primes[i] = 1; int sqrt = (int)Math.sqrt(N) +1; for(int i=2; i<=N;i++){ if(primes[i] == 0) continue; else System.out.println(i); if(i>sqrt) continue; //大于n平方根的就不用向后标记了,为什么呢?假设合数X<N,X的最小质因数x<sqrt(N),X一定会被x标记为合数 for(int j=i<<1; j<=N; j+=i){ primes[j] = 0; } } }
- 二次筛选:针对求L到U之间的所有素数问题。使用一次筛选方法空间开销有些大,因此可以先求出sqrt(U)以内的素数,然后用这些素数过滤掉L到U之间的合数即可。时间复杂度O(U^0.5+U-L),空间复杂度O(U^0.5+U-L)。
public static void printPrimes(int L, int U){ int sqrtU = (int)Math.sqrt(U)+1; byte[] lPrimes = new byte[sqrtU+1]; for(int i=2; i<=sqrtU; i++) lPrimes[i] = 1; //1次筛选 int sqrt = (int)Math.sqrt(sqrtU)+1; for(int i=2;i<=sqrt; i++){ if(lPrimes[i]==0) continue; for(int j=i<<1; j<=sqrtU; j+=i){ lPrimes[j] = 0; } } //2次筛选 byte[] primes = new byte[U-L+1]; for(int i=0; i<primes.length; i++) primes[i] = 1; for(int i=2; i<=sqrtU; i++){ if(lPrimes[i] == 0) continue; //素数 int base = L/i+1; if(L%i ==0) base--; base = base * i; //大于等于L的p的最小倍数 while(base<=U){ primes[base-L] = 0; base += i; } } for(int i=0; i<primes.length; i++) if(primes[i]==1) System.out.println(i+L); }