浅析求素数算法
时间: 2006-10-27
注意: 如果没有特殊说明, 以下讨论的都是针对n为素数时的时间复杂度
1. 根据概念判断:
如果一个正整数只有两个因子, 1和p,则称p为素数.
代码:
bool isPrime(int n)
{
if(n < 2) return false;
for(int i = 2; i < n; ++i)
if(n%i == 0) return false;
return true;
}
时间复杂度O(n).
2. 改进, 去掉偶数的判断
代码:
bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;
for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;
return true;
}
时间复杂度O(n/2), 速度提高一倍.
3. 进一步减少判断的范围
定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
证明: 如果n不是素数, 则由定义n有一个因子d满足1<d<n.
如果d大于sqrt(n), 则n/d是满足1<n/d<=sqrt(n)的一个因子.
代码:
bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;
for(int i = 3; i*i <= n; i += 2)
if(n%i == 0) return false;
return true;
}
时间复杂度O(sqrt(n)/2), 速度提高O((n-sqrt(n))/2).
4. 剔除因子中的重复判断.
例如: 11%3 != 0 可以确定 11%(3*i) != 0.
定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
证明: I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
I2. 如果d是素数, 则定理得证, 算法终止.
I3. 令n=d, 并转到步骤I1.
由于不可能无限分解n的因子, 因此上述证明的算法最终会停止.
代码:
// primes[i]是递增的素数序列: 2, 3, 5, 7, ...
// 更准确地说primes[i]序列包含1->sqrt(n)范围内的所有素数
bool isPrime(int primes[], int n)
{
if(n < 2) return false;
for(int i = 0; primes[i]*primes[i] <= n; ++i)
if(n%primes[i] == 0) return false;
return true;
}
假设n范围内的素数个数为PI(n), 则时间复杂度O(PI(sqrt(n))).
函数PI(x)满足素数定理: ln(x)-3/2 < x/PI(x) < ln(x)-1/2, 当x >= 67时.
因此O(PI(sqrt(n)))可以表示为O(sqrt(x)/(ln(sqrt(x))-3/2)),
O(sqrt(x)/(ln(sqrt(x))-3/2))也是这个算法的空间复杂度.
5. 构造素数序列primes[i]: 2, 3, 5, 7, ...
由4的算法我们知道, 在素数序列已经被构造的情况下, 判断n是否为素数效率很高;
但是, 在构造素数序列本身的时候, 是否也可是达到最好的效率呢?
事实上这是可以的! -- 我们在构造的时候完全可以利用已经被构造的素数序列!
假设我们已经我素数序列: p1, p2, .. pn
现在要判断pn+1是否是素数, 则需要(1, sqrt(pn+1)]范围内的所有素数序列,
而这个素数序列显然已经作为p1, p2, .. pn的一个子集被包含了!
代码:
// 构造素数序列primes[]
void makePrimes(int primes[], int num)
{
int i, j, cnt;
primes[0] = 2;
primes[1] = 3;
for(i = 5, cnt = 2; cnt < num; i += 2)
{
int flag = true;
for(j = 1; primes[j]*primes[j] <= i; ++j)
{
if(i%primes[j] == 0)
{
flag = false; break;
}
}
if(flag) primes[cnt++] = i;
}
}
makePrimes的时间复杂度比较复杂, 而且它只有在初始化的时候才被调用一次.
在一定的应用范围内, 我们可以把近似认为makePrimes需要常数时间.
在后面的讨论中, 我们将探讨一种对计算机而言更好的makePrimes方法.
6. 更好地利用计算机资源...
当前的主流PC中, 一个整数的大小为2^32. 如果需要判断2^32大小的数是否为素数,
时间: 2006-10-27
注意: 如果没有特殊说明, 以下讨论的都是针对n为素数时的时间复杂度
1. 根据概念判断:
如果一个正整数只有两个因子, 1和p,则称p为素数.
代码:
bool isPrime(int n)
{
if(n < 2) return false;
for(int i = 2; i < n; ++i)
if(n%i == 0) return false;
return true;
}
时间复杂度O(n).
2. 改进, 去掉偶数的判断
代码:
bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;
for(int i = 3; i < n; i += 2)
if(n%i == 0) return false;
return true;
}
时间复杂度O(n/2), 速度提高一倍.
3. 进一步减少判断的范围
定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
证明: 如果n不是素数, 则由定义n有一个因子d满足1<d<n.
如果d大于sqrt(n), 则n/d是满足1<n/d<=sqrt(n)的一个因子.
代码:
bool isPrime(int n)
{
if(n < 2) return false;
if(n == 2) return true;
for(int i = 3; i*i <= n; i += 2)
if(n%i == 0) return false;
return true;
}
时间复杂度O(sqrt(n)/2), 速度提高O((n-sqrt(n))/2).
4. 剔除因子中的重复判断.
例如: 11%3 != 0 可以确定 11%(3*i) != 0.
定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
证明: I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
I2. 如果d是素数, 则定理得证, 算法终止.
I3. 令n=d, 并转到步骤I1.
由于不可能无限分解n的因子, 因此上述证明的算法最终会停止.
代码:
// primes[i]是递增的素数序列: 2, 3, 5, 7, ...
// 更准确地说primes[i]序列包含1->sqrt(n)范围内的所有素数
bool isPrime(int primes[], int n)
{
if(n < 2) return false;
for(int i = 0; primes[i]*primes[i] <= n; ++i)
if(n%primes[i] == 0) return false;
return true;
}
假设n范围内的素数个数为PI(n), 则时间复杂度O(PI(sqrt(n))).
函数PI(x)满足素数定理: ln(x)-3/2 < x/PI(x) < ln(x)-1/2, 当x >= 67时.
因此O(PI(sqrt(n)))可以表示为O(sqrt(x)/(ln(sqrt(x))-3/2)),
O(sqrt(x)/(ln(sqrt(x))-3/2))也是这个算法的空间复杂度.
5. 构造素数序列primes[i]: 2, 3, 5, 7, ...
由4的算法我们知道, 在素数序列已经被构造的情况下, 判断n是否为素数效率很高;
但是, 在构造素数序列本身的时候, 是否也可是达到最好的效率呢?
事实上这是可以的! -- 我们在构造的时候完全可以利用已经被构造的素数序列!
假设我们已经我素数序列: p1, p2, .. pn
现在要判断pn+1是否是素数, 则需要(1, sqrt(pn+1)]范围内的所有素数序列,
而这个素数序列显然已经作为p1, p2, .. pn的一个子集被包含了!
代码:
// 构造素数序列primes[]
void makePrimes(int primes[], int num)
{
int i, j, cnt;
primes[0] = 2;
primes[1] = 3;
for(i = 5, cnt = 2; cnt < num; i += 2)
{
int flag = true;
for(j = 1; primes[j]*primes[j] <= i; ++j)
{
if(i%primes[j] == 0)
{
flag = false; break;
}
}
if(flag) primes[cnt++] = i;
}
}
makePrimes的时间复杂度比较复杂, 而且它只有在初始化的时候才被调用一次.
在一定的应用范围内, 我们可以把近似认为makePrimes需要常数时间.
在后面的讨论中, 我们将探讨一种对计算机而言更好的makePrimes方法.
6. 更好地利用计算机资源...
当前的主流PC中, 一个整数的大小为2^32. 如果需要判断2^32大小的数是否为素数,