如何判断素数一个素是不是素数呢?或许你会以为这是一个非常简单问题,就像1+1=2一样,当一个数的因子只有1和它本身的时候就是素数,很简单的嘛!!!但是,当一个数特别大的时候就没有那么简单进行判断了。
下面我们就求素数常用的一些方法进行讨论和判断。
我们先来看看幼稚园的小孩子的做法:
#include<stdio.h>
#include<time.h>
int IsPrime(int n){
int i;
if(n <= 1){
return 0;
}
if(n==2){
return 1;
}
for(i = 2;i < n;i++){
if(n%i==0) return 0;
}
return 1;
}
int main(){
int n,i;
int t1 = clock();
for(i = 0;i<=100000;i++){
if(IsPrime(i)) printf(" %d ",i);
}
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
一个数去除以比它的一半还要大的数,一定除不尽的,这还用判断吗??
很容易发现的,这种方法判断素数,对于一个整数n,需要n-2次判断,时间复杂度是O(n)在n非
常大或者测试量很大的时候,这种笨蛋做法肯定是不可取的。
下面我们来看稍微聪明一点点的幼稚园的做法:
#include<stdio.h>
#include<time.h>
int IsPrime(int n){
int i;
if(n <= 1){
return 0;
}
if(n==2){
return 1;
}
for(i = 2;i <= n/2;i++){
if(n%i==0) return 0;
}
return 1;
}
int main(){
int n,i;
int t1 = clock();
for(i = 0;i<=100000;i++){
if(IsPrime(i)) printf(" %d ",i);
}
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
因为2*3=6和3*2=6效果是一样的,所以我们只要对它的前一半的数进行判断就可以了,后面的就可以不用判断了。
下面我们来看比较聪明的幼稚园做法:
#include<stdio.h>
#include<time.h>
int IsPrime(int n){
int i;
if(n==2){
return 1;
}
if(n <= 1||n%2==0){
return 0;
}
for(i = 3;i <= n/2;i+=2){
if(n%i==0) return 0;
}
return 1;
}
int main(){
int n,i;
int t1 = clock();
for(i = 0;i<=100000;i++){
if(IsPrime(i)) printf(" %d ",i);
}
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
我们知道除了0和2以外的偶数都不是素数,那么素数就只能在奇数中出现,所以我们可以先把所有偶数全部去掉,然后判断奇数是不是偶数
我们继续看小学生的做法,幼稚园升级为小学生了
#include<stdio.h>
#include<time.h>
int IsPrime(int n){
int i;
for(i = 3;i <= n/2;i+=2){
if(n%i==0) return 0;
}
return 1;
}
int main(){
int n,i;
int t1 = clock();
printf(" 2 ");
for(i = 3;i<=100000;i+=2){
if(IsPrime(i)) printf(" %d ",i);
}
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
接下来我们看比较聪明的小学生的做法:
#include<stdio.h>
#include<time.h>
#include<math.h>
int IsPrime(int n){
int i;
if(n%2==0) return 0;
for(i = 3;i <= sqrt(n);i+=2){
if(n%i==0) return 0;
}
return 1;
}
int main(){
int n,i;
int t1 = clock();
printf(" 2 ");
for(i = 3;i<=100000;i++){
if(IsPrime(i)) printf(" %d ",i);
}
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
对于一个小于n的整数X,如果n不能整除X,则n必定不能整除n/X。反之相同
一个明显的优化,就是只要从2枚举到√n 即可。
因为在判断2的同时也判断了n/2。到√n时就把2到n-1都判断过了。
做一个测试发现,如果是这样额话,每一次判断都要计算i*i,
而如果只调用sqrt()函数的话,只需要计算一次。故还是sqrt()函数好一些啊。
对于一个整数N,需要测试√n-1 次,所以本算法的时间复杂度O(√n)。确实,对于一个小学生,能够这么牛逼的想到这么多优化,
已经很强大了。
不过其实没什么必要。。。。。。
这里的i+=2,是因为,偶数除了2之外,是不可能是素数的、
所以从3开始,直接+2 。进一步优化。
这个大概就是朴素判断素数方法的最佳优化了。(也许你还有更好的优化)
所以,如果是对于一般的素数判断的话,用上面那个代码吧
小学生毕业了,到了中学,会有怎样的成长呢?
下面来看看中学生们是怎么样判断的。
埃拉托色尼选筛法(the Sieve of Eratosthenes)简称埃氏筛法,是古希腊数学家埃拉托色尼
(Eratosthenes 274B.C.~194B.C.)提出的一种筛选法。
BY:Lw
是针对自然数列中的自然数而实施的,用于求一定范围内的质数,
它的容斥原理之完备性条件是p=H~。
可参考:
http://zh.wikipedia.org/wiki/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89
%B9%E5%B0%BC%E7%AD%9B%E6%B3%95
基本原理:
筛素数的基本方法是用来筛选出一定范围内的素数素数筛法的基本原理,利用的是素数p只有1和p
这两个约数,并且一个数的约数一定不大于本身,素数筛法的过程:
把从1开始的、某一范围内的正整数从小到大顺序排列,
1不是素数,首先把它筛掉。
剩下的数中选择最小的数是素数,然后去掉它的倍数。
依次类推,直到筛子为空时结束。
求解用途:
素数筛法经常作为一道题的一部分用来打一定范围内素数表,
然后利用素数表作为基础解题。
#include<stdio.h>
#include<time.h>
#include<math.h>
#define N 100000
int prime[100000];
void init(){
int i,j;
for(i = 2;i <=N ;i++){
prime[i] = 1;
}
for(i = 2;i <= N;i++){
if(prime[i]) printf(" %d ",i);
for(j = i+i;j <= N;j += i) prime[j] = 0;
}
}
int main(){
int n,i;
int t1 = clock();
init();
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
执行完本算法之后,prime[i]中如果是1,表示i为素数,0,表示i不是素数。
所以呢,这个算法执行完一遍之后,就可以在O(1)的时间内判断出N以内的任意数,是不是素数
了,所以这个算法消耗的时间可以说全部在筛选上了。初看这个算法,会觉得这个算法的时间复杂
度是O(N^2),,但其实不是的,在第二个循环中,每次递增的i,当i越来也大的时候,j很快就能超
过N的,筛选法的实际复杂度是O(n*log(logn))。
到这里你可能会说中学生也不过如此嘛,运行效率和聪明的小学生差不多,不要急,这只是最基础的中学生做法
下面我们一起来看看比较有思想的中学生的做法:
#include<stdio.h>
#include<time.h>
#include<math.h>
#define N 100000
int prime[100000];
int a[N];
void init(){
int i,j,len=0;
for(i = 0;i <N ;i++){
prime[i] = 0;
}
for(i = 2; i <= N; i++)
{
if(!prime[i]){
a[len++] = i;
printf(" %d ",i);
}
for(j = 0; j< len &&a [j]*i <= N; j++)
{
prime[a[j]*i] = 1;
if(i % a[j] == 0) break;
}
}
}
int main(){
int n,i;
int t1 = clock();
init();
int t2 = clock();
printf("\n运行时间:%d\n",t2-t1);
}
这个算法的关键在于if(i % a[j] == 0) break;,它使得任何一个合数,只能被它最小的质因数标记过
一次,再一次进行优化。所以整个算法是线性的。但考虑到log(log(100000000))还不到3,
故这个线性算法其实也只有理论的价值罢了。
其实我不这样认为。这样其实可以当成素数表来用,因为定义了一个数组p,存放的都是素数。
讲到这里,你应该对判断素数这个问题有了一个新的认识了。
既要考虑时间上的问题。又要考虑空间上的问题。
也就是这并不是一个无脑问题。