相关链接
PTA 181 求因子和
C++格式化输出 微秒级计时 素数筛算法 因子和公式
直接上结论
- 直接使用 Sqrt(n) 为循环上限的方法,时间稳定O(sqrt(n)),程序运行不会超时
- 素数筛法理论复杂度小于O(sqrt(n)),在因子远小于n的情况下能达到最佳效果
- 由于算法相对复杂,素数筛法的运行时间会更高
- 在大数区间 [108,109] 内随机选取的平均运算时间是 素数筛: 开方法 ≈ 2: 1
- 部分运行结果可见下图,前4个数为比较有代表性的测试数字,最后为4中的平均时间
主要思路
首先可以确定本题直接使用以开方为上限的循环足以在规定时间内完成题目,于是考虑是否可以用更快的算法实现。涉及因数的题目首先想到的算法就是素数筛法,其可以在线性时间内一定选出范围内的素数,借助因子和公式可以实现因子和的快速计算。而且使用素数筛可以只求小于sqrt(n)的素数,因为n除以这些素因数后剩下的一定是素数且次数为1:若其次数大于一,则其乘方大于n矛盾;若其为合数,其一定包含一个小于sqrt(n)的素因数矛盾。因此素数筛可以通过计算sqrt(n)以内的素数来完成因数和的计算。
//开方法
int sumOfFactor_Sqrt( int n ){
int sum = 1, i;
for( i = 2; i < n/i; i++ ){
if(n%i==0)
sum += i + n/i;
}
return n % i? sum: sum+i;
}
然而素数筛法会在筛素数的过程消耗大量时间,在不优化的情况下其复杂度和开方法一样都是 O(sqrt(n)),优化思路落在了循环上限上。由于不需要求得每个因子的值,可以在求出每一个素因子后让n除去改素因子,通过降低n的值降低循环的次数。因此,根据上一段中关于最后一个素数的判断方法,设n的最大素因子为p,次数为q,则素数筛法理论时间复杂度为 O( max( log(n), sqrt(pq) ) )
int sumOfFactor_Sieve( int n ){
int sum = 1, num = n;
for( int i = 2; i <= n/i; i++ ){
if( !isNotPrime[i] ){
primes[pCnt++] = i;
int v = 1;
while( n % i == 0 ){
v = v*i+1;
n /= i;
}
sum *= v;
}
for( int j = 0; j < pCnt && i*primes[j] <= n/i/primes[j]; j++ )
isNotPrime[i*primes[j]] = true;
}
if( n > 1 ) sum *= ( n+1 );
sum -= num;
return sum;
}
完整实验程序代码
#include <iostream>
#include <iomanip>
#include <ctime>
#include <windows.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
int primes[100000];
int pCnt = 0;
bool isNotPrime[100000] = {
0};
void