算法优化

Optimization of Algorithm

本文关键字:优化 算法      更新时间:2023-10-16

这是问题的链接。

该问题询问形式为 1/x + 1/y = 1/z 的丢番图方程的解数(其中 z = n!重新排列给定的方程清楚地表明答案是 z2 的因子数。

所以问题归结为找到 n 的因子数2n 阶乘平方)。

我的算法如下

  1. 使用埃拉托色尼筛算法为所有素数 <= n 创建一个布尔查找表。
  2. 遍历所有素数 P <= n,并在 n! 中找到它的指数。 我使用步进函数公式执行此操作。设指数为 K,然后是 n 中 P 的指数2 将是 2K
  3. 使用步骤 2 使用标准公式计算因子数。

对于 n = 106 的最坏情况输入,我的 c 代码在 0.056s 内给出答案。我想复杂性不会比O(n lg n)大。但是当我在网站上提交代码时,我只能通过 3/15 个测试用例,其余的测试用例在超过时间限制时做出裁决。

我需要一些提示来优化此算法。

到目前为止的代码:

#include <stdio.h>
#include <string.h>
#define ULL unsigned long long int
#define MAXN 1000010
#define MOD 1000007
int isPrime[MAXN];
ULL solve(int N) {
    int i, j, f;
    ULL res = 1;
    memset(isPrime, 1, MAXN * sizeof(int));
    isPrime[0] = isPrime[1] = 0;
    for (i = 2; i * i <= N; ++i)
        if (isPrime[i])
            for (j = i * i; j <= N; j += i)
                isPrime[j] = 0;
    for (i = 2; i <= N; ++i) {
        if (isPrime[i]) {
            for (j = i, f = 0; j <= N; j *= i)
                f += N / j;
            f = ((f << 1) + 1) % MOD;
            res = (res * f) % MOD;
        }
    }
    return res % MOD;
}
int main() {
    int N;
    scanf("%d", &N);
    printf("%llun", solve(N));
    return 0;
}
这里有

一个int溢出:

for (j = i, f = 0; j <= N; j *= i)

如果46340 < i < 65536并且对于许多较大的i,在第二次迭代中,如果溢出环绕,j将为负数(这是未定义的行为,因此任何事情都可能发生)。

将其替换为例如

for(j = N / i, f = 0; j > 0; j /= i) {
    f += j;
}

但是,由于溢出而导致的额外迭代不太可能导致"超出时间限制",这很可能只会导致错误的结果。

所以通用建议是

  • 只筛奇数,也许也可以从筛子中消除 3 的倍数。从筛子中消除奇数大约使筛分所需的时间减半。
  • 不要使用整个int作为标志,使用位或至少char秒。这提供了更好的缓存局部性和进一步的加速。