- 质数只能被1和自己本身整除的数
- 如:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97...
方法1:时间复杂度O(n)
- 最直观的方法,根据定义,因为质数除了1和本身之外没有其他约数,所以判断n是否为质数,根据定义直接判断从2到n-1是否存在n的约数即可
function isPrime(num){
if(num ==2 || num==3)
return true;
for (var i = 2;i <= n-1;i++) {
if(num % i ==0){
return false;
}
}
return true;
}
方法2:时间复杂度O(logN)
- 上述判断方法,明显存在效率极低的问题。对于每个数n,其实并不需要从2判断到n-1
- 我们知道,一个数若可以进行因数分解,那么分解时得到的两个数,一定是一个小于等于sqrt(n)和一个大于等于sqrt(n)
- 据此,上述代码中并不需要遍历到n-1,遍历到sqrt(n)即可,因为若sqrt(n)左侧找不到约数,那么右侧也一定找不到约数
12
1×12=12
2×6=12
3×4=12
4×3=12
- 能被2整除的数一定不是素数,所以不用被4、6整除,这样能减少n/2次执行次数
- 能被3整除的数一定不是素数,所以不用被6、9整除,这样能继续减少执行次数
function isPrime(num){
if(num ==2|| num==3 )
return true;
var temp = parseInt(Math.sqrt(num));
for (var i = 2;i <= temp;i++) {
if(num % i ==0){
return false;
}
}
return true;
}
方法3:时间复杂度
- 2x,3x,5x肯定不是质数
- 此时判断质数可以2个为单元快进,即在循环中i++步长加大为2,加快判断速度
function isPrime(num){
if(num ==2 || num==3)
return true;
if(num % 2 == 0 || num % 3 == 0 || num % 5 == 0)
return false;
var temp = parseInt(Math.sqrt(num));
for (var i = 7;i <= temp;i=i+2) {
if(num % i == 0){
return false;
}
}
return true;
}
方法4:时间复杂度
- 证明:令x≥1,将大于等于5的自然数表示如下:
- ······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······
- 可以看到,6的倍数两侧之外的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们一定不是素数,再加上6x本身也不是素数
- 所以在6的倍数两侧的一定是质数,及:if(num %6!= 1&&num %6!= 5) return false ;
- 在6的倍数相邻两侧并不是一定就是质数
- 此时判断质数可以6个为单元快进,即在循环中i++步长加大为6,加快判断速度
- 原因是,假如要判定的数为n,则n必定是6x-1或6x+1的形式,对于循环中6i-1,6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被6i,6i+2,6i+4整除,则n至少得是一个偶数,但是6x-1或6x+1的形式明显是一个奇数,故不成立
- 另外,如果n能被6i+3整除,则n至少能被3整除,但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成立。
- 综上,循环中只需要考虑6i-1和6i+1的情况,即循环的步长可以定为6,每次判断循环变量k和k+2的情况即可,理论上讲整体速度应该会是方法(2)的3倍
function isPrime(num){
//两个较小数
if(num == 2 || num == 3 )
return true ;
//不在6的倍数两侧的一定不是质数
if(num%6 != 1 && num%6 != 5){
return false ;
}
var tmp =Math.sqrt(num);
//在6的倍数两侧的也可能不是质数
for(var i = 5;i <= tmp; i+=6 )
if(num%i == 0 || num%(i+2) == 0)
return false ;
return true;
}