复杂度为O(n)的1~n因数之和
目录
一、题目描述
二、传统写法
三、优化分析
四、代码演示
一、题目描述
题目: 输入一个数字n,求出这个数从1~n的所有因数之和
输入样例:
12
输出样例:
87
二、传统写法
所谓传统写法就暴力枚举,将每一个数字都提出所有的因子,然后进行相加统计,获得最终结果,方式如下:
int function(const int n)
{
long long sum = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (i%j == 0)sum += j;
}
}
return sum;
}
这种算法的复杂度是很高的,所用的时间复杂度为O(n2)。所以就出现了下面这种优化的方案:
int function(const int n)
{
long long sum = 0;
for (int i = 1; i <= n; i++) // 时间复杂度为:O(n)
{
for (int j = 1; j * j <= i; j++) // 时间复杂度为:O(根号n)
{
if (i % j == 0)
{
sum += j;
if (j * j == i)continue; //break和continue都一样
sum += i / j;
}
}
}
return sum;
}
这种优化的灵感是来自当 i*j==n
时,相当于找到了两个因子,所以这里选择开方的方式判断,时间复杂度为0(n根号n),对比前一个算法来说节省了不少时间。不过复杂度还是很大,当n<=107时,计算时间将会超过1秒。
二、优化分析
比起暴力的枚举不同,这个算法使用的是1~n中每个因子提供的次数所算出来的,我们假设n=6,如下图:
上面表格中,因子1在1~n中存在的数量是6,具体存在的数字是1 2 3 4 5 6;同样理解,因子2在1~n中存在的数量是3,具体存在的数字是2 4 6。我们知道各个因子出现的次数之后,将每因子次数加起来再统计,最终结果就是我们所需要的因数之和,公式为i*(n/i)。
公式i×(n/i)
的由来: i即时因子从1~n的每个因子;(n/i)即是计算因子数量,需要注意这里若i=4时,为什么结果为1而不是浮点数,第一是两个整数计算后还是整数,第二是若是两个,则这里存在它的数字就是4 8,这超出了n的最大值。
二、代码演示
下面是优化后的解法:
int function(const int n)
{
long long sum = 0;
for (int i = 1; i <= n; i++)
{
sum += i * (n / i);
}
return sum;
}
这种算法的时间复杂度是O(n),优化成了线性阶,当n<=109时,计算的时间也是很理想的。
初学小白,若有不足的地方还请大佬指正,谢谢!