需要一种方法来使此代码运行得更快

Need a way to make this code run faster

本文关键字:代码 运行 方法 一种      更新时间:2023-10-16

我正在尝试解决欧拉计划问题 401。他们唯一能找到解决它的方法就是蛮力。我已经运行了这段代码大约 10 分钟,没有任何答案。谁能帮我提出改进的想法。

法典:

#include <iostream>
#include <cmath>
#define ull unsigned long long
using namespace std;
ull sigma2(ull n);
ull SIGMA2(ull n);
int main()
{
ull ans = SIGMA2(1000000000000000) % 1000000000;
cout << "Answer: " << ans << endl;
cin.get();
cin.ignore();
return 0;
}
ull sigma2(ull n)
{
ull sum = 0;
for(ull i = 1; i<=floor(sqrt(n)); i++)
{
if(n%i == 0)
{
sum += (i*i)+((n/i)*(n/i));
}
if(i*i == n)
{
sum -= n;
}
}
return sum;
}
ull SIGMA2(ull n)
{
ull sum = 0;
for(ull i = 1; i<=n; i++)
{
sum+=sigma2(i);
}
return sum;
}

如果 a/b=c,你缺少一些分隔符,并且ba的分隔符,那么c也将是a的分隔符,但c可能大于floor(sqrt(a)),例如 3> floor(sqrt(6)) 但除以 6。

然后你应该把你的floor(sqrt(n))放在一个变量中,并在for中使用变量,否则你重新计算它,这是非常昂贵的每个操作。

您可以进行一些简单的优化:

  • 内联sigma2
  • 在循环之前计算floor(sqrt(n))(但编译器可能无论如何都会这样做),
  • 预先计算从1n的所有整数的平方,然后使用数组查找而不是乘法

通过改变你的方法,你会获得更多。想想你想做什么 - 从 1 到 n 的所有整数的所有除数的平方求和。您按除数的划分对除数进行分组,但您可以在此总和中重新分组项。让我们按除数的值对除数进行分组:

  • 1将所有内容除以,因此它将在总和中出现n次,得出1*1*n总数,
  • 2除以偶数,将出现n/2(整数除法!),得到2*2*(n/2)总数,
  • k......将带来k*k*(n/k)总数。

因此,我们应该将k*k*(n/k)1k加起来n.

考虑一下问题。

您尝试的暴力破解方式显然不是一个好主意。

你应该想出更好的东西... 没有任何方法可以使用一些不错的素数分解方法来加快计算速度吗?没有任何递归模式吗?试着找点东西...

您可以执行的一个简单优化是,数字中将有许多重复的因素。

所以首先估计有多少个数字 1 是一个因子(所有 N 个数字)。
在多少个数字中,2 是一个因子 ( N/2 )。

其他人也是如此。

只需将它们的平方乘以它们的频率即可。

然后,时间复杂度应立即降低到O(N)

有明显的微优化,例如++i而不是i++或将floor(sqrt(n))从循环中取出(这是两个浮点运算,与循环中的其他整数运算相比非常昂贵),并且只计算n/i一次(使用虚拟变量,然后计算虚拟人的平方)。

算法中也有相当明显的简化。例如 SIGMA2(i) = SIGMA2(i-1) + sigma2(i)。但是不要使用递归,因为你需要一个非常大的数字,这是行不通的,你的堆栈内存会耗尽。使用循环而不是递归。改进的潜力很大。

好吧,还有一个更大的问题 - 10^15 有 15 位数字。这个数字的平方有 30 位数字。你不可能把它存储到无符号的长长,我想到大约 20 位数字。所以你需要以某种方式使用模 10^9(作业的结束)并获得额外的空间来计算......

例如,使用蛮力时,每隔百万个数字打印出临时结果,让您了解接近最终结果的速度。盲目等待10分钟不是一个好主意。