LeetCode 第 204 题 (Count Primes)
Description:
Count the number of prime numbers less than a non-negative number, n.
计算小于 N 的素数的个数。这道题目比较简单。但是想提高计算效率与需要费点脑筋。
判断一个数字 n 是不是素数的简单方法是 用 n 去除 2,3,4,…,n?1,如果都不能整除就说明这个数是素数。
按照这个思路可以写个简单的函数。
bool isPrime(int n)
{
if(n < 1) return false;
for (int i = 2; i < n; i++)
{
if (num % i == 0)
{
return false;
}
}
return true;
}
这个代码还可以优化一点。不用除到 n?1,只要除到 [n??√] 就可以了。
bool isPrime(int n)
{
if(n < 1) return false;
for (int i = 2; i * i < n; i++)
{
if (num % i == 0)
{
return false;
}
}
return true;
}
这个代码每次判断时都要算一遍 i?i,而第 69 题给出了一个计算 [n??√] 的快速算法,利用这个算法还可以再优化一点。
int mySqrt(int x)
{
if(x <= 0) return 0;
int a1 = 1;
int a2 = 46341 * 2 - 1;
unsigned int a, y;
if(a2 > x / 2) a2 = x;
do
{
a = (a1 + a2) / 2;
y = a * a;
if(y == x) return a;
if(y > x)
{
a2 = a;
}
else
{
a1 = a;
}
}while(a1 + 1 < a2);
a = (a1 + a2) / 2;
return a;
}
bool isPrime(int n)
{
if(n < 1) return false;
int sn = mySqrt(n);
for (int i = 2; i <= sn; i++)
{
if (num % i == 0)
{
return false;
}
}
return true;
}
之后计算素数个数的代码就可以写成这样。
int countPrimes(int n)
{
int count = 0;
for (int i = 1; i < n; i++)
{
if (isPrime(i)) count++;
}
return count;
}
即使写成这样,这个代码的计算复杂度还是有点大,isPrime 函数的时间复杂度是 O(n??√),countPrimes 函数的时间复杂度是 O(n),所以整体的复杂度是 O(nn??√)。
实际上有一种简单的算法,时间复杂度可以降低到 O(nlog(log(n)))。这种方法就是所谓的 Eratosthenes 筛选法(Sieve of Eratosthenes)。首先建立一张有 n 个元素的大表,表中每个元素都标记为 1。那么这个表中 2 是第一个素数,2 的倍数都不是素数,那么把表中 2 的倍数都标记为 0。之后找表中在 2 后面第一个元素值为 1 的元素,这个元素就是第二个素数,当然这个数是 3,然后再将表中3 的倍数全部标记为 0。重复这个过程,就可以将表中所有的素数找出来了。按照这个思路写的代码如下:
int countPrimes(int n)
{
bool *primeTable = new bool[n];
for (int i = 0; i < n; i++)
{
primeTable[i] = true;
}
int i = 2;
int count = 0;
while (i * i < n)
{
if (primeTable[i])
{
count++;
for (int j = i * i; j < n; j += i)
{
primeTable[j] = false;
}
}
i++;
}
while (i < n)
{
if(primeTable[i])
{
count++;
}
i++;
}
delete[] primeTable;
return count;
}
除此之外,还有更快的判段一个数是否是素数的方法,时间复杂度为 O(1)。不过那种算法属于概率算法,有一定的小概率出错,以后有时间单独写一篇博客介绍那些方法。