【问题标题】:Need to improve Running Time Complexity?需要提高运行时间复杂度?
【发布时间】:2019-01-17 11:39:25
【问题描述】:
#include<iostream>
using namespace std;
int gcd(int a, int b, int res);
int main()
{
    int res = 1;
    int n, i, ret;
    int count = 1;
    cin >> n;
    for (i = 2; i < n; i++)
    {
        ret = gcd(n, i, res);
        if (ret == 1)
            count++;
    }
    cout << count;
    return 0;
}
int gcd(int a, int b, int res)
{
    if (a == b)
        return res * a;
    else if ((a % 2 == 0) && (b % 2 == 0))
        return gcd(a / 2, b / 2, 2 * res);
    else if (a % 2 == 0)
        return gcd(a / 2, b, res);
    else if (b % 2 == 0)
        return gcd(a, b / 2, res);
    else if (a > b)
        return gcd(a - b, b, res);
    else
        return gcd(a, b - a, res);
}

请解释我需要纠正什么,比如我可以使用 scanf 而不是 cin? 另一个条件是:- 输入的唯一行是一个整数 N,它不能被任何大于 13 的素数整除。这种情况会影响我的 TLE 吗?

实际问题是:

倒数 问题描述 每个人都知道乘法 mod n,其中 n 是一个正整数。两个正整数 a 和 b mod n 的乘积是乘积除以 n 的余数。

如果有一个小于 n 的正整数 x 使得 a 和 x mod n 的乘积为 1,则称 a 与 n 有乘法逆元。

伟大的数学家欧拉证明了每一个小于 n 且与​​ n 互质的正整数(没有除 1 以外的 n 的公因数)关于 n 的乘法逆。

这个问题是找到小于n的正整数的个数,这些正整数与n有乘法逆元

约束 N

输入格式 输入的唯一行是一个整数 N,它不能被任何大于 13 的素数整除。

输出 一行包含一个整数,给出小于 N 且具有乘法逆元的整数个数

解释 示例 1

输入

20

输出

8

解释

N=20

如果我们列出小于 20 且除 1 之外与 20 没有公因数的数,它们是

1、3、7、9、11、13、17、19

因为它们有 8 个,所以有 8 个小于 20 的数与 20 有乘法逆元。因此结果是 8。

示例 2

输入

36

输出

12

解释

N=36。有 12 个小于 36 的数除了 1 和 36 之外没有公因数。这些是

1、5、7、11、13、17、19、23、25、29、31、35

因此,正如 Euler 所证明的,有 12 个小于 36 的数与 36 有乘法逆元。因此输出为 12。 https://i.stack.imgur.com/cvM0l.png

【问题讨论】:

  • 我可以使用 scanf 代替 cin? 这通常不会修复 TLE。 TLE 通常是因为您的算法太慢了。您的输入是否需要多几微秒才能读取并不重要。
  • 不要费心使用 C I/O。与普遍的看法相反,它并不比cincout 快​​,特别是如果您禁用与stdio 的同步并且您只读取一个整数。你的算法太慢了,但如果我们不知道你在做什么,我们很难帮助你改进它。
  • “我的算法需要 O(logn) 时间复杂度。”for (i = 2; i &lt; n; i++) 矛盾。
  • 关于我的算法需要 O(logn) 时间复杂度,我建议在gcd 中弹出一个计数器并测试该断言。
  • 我非常怀疑O(log n) 算法是否会 TLE。如果您的算法使用类似 10 * log2(n) 的操作,n 必须是类似 2^50000000 的操作,才能总共进行 5 亿次操作,这是您的普通计算机在一秒钟内可以完成的操作(实际数字可能更多)。另一种思考方式是,log2(10^9) 大约是 30,如前所述,计算机每秒至少可以执行 5 亿次操作。

标签: c++


【解决方案1】:

首先,当 C++17&lt;numeric&gt; 中包含 std::gcd 时,为什么要实现自己的 gcd。您的 GCD 实现具有相同的 O(log N) 复杂度,但由于大量条件,效率非常低:

对于 N=100000000(1 亿),您的算法在我的机器上运行 26 秒。使用std::gcd() 相同的运行时间为 15 秒。这更好,但不是很多。

如果你没有 C++17,那么:

int gcd(int a, int b)
{
    if (b < a)
        std::swap(a, b);
    while (a != 0) {
        b %= a;
        std::swap(a, b);
    }
    return b;
}

提供与std::gcd() 相同的性能。

但这仍然太慢了。更好的方法是分析工作。 您想计算范围 [1..N] 中与 N 没有公因数的整数的数量。如果不实际枚举所有值,这很简单。首先,找出 N 的所有质因数。

假设N=a^m * b^n * c^k(对于 m,n,k >= 1)。 范围内的 N * (a-1) / a 值没有 a 作为因素。其中,有N * (a-1) / a * (b-1) / b 没有b 作为一个因素,依此类推。这只是因为 a,b,c 是素数,也是 N 的因数。否则除法将不会不准确或不会是整数。

这使得代码非常快:

#include<iostream>
int main()
{
    unsigned n;
    std::cin >> n;
    if (!std::cin) {
        std::cout << "Bad input\n";
        return 1;
    }
    unsigned count = n;
    unsigned factors_left = n;
    // i is long long to avoid overflow for i * i, 
    // where n is a big prime
    for (unsigned long long i=2 ; i * i <= factors_left ; ++i) {
        if (factors_left % i == 0) {
            while (factors_left % i == 0)
                factors_left /= i;
            count -= count / i;
        }
    }
    if (factors_left > 1)
        // factors_left is a prime number
        count -= count / factors_left;

    std::cout << count << "\n";
    return 0;
}

复杂度 = O(sqrt(n))。最坏的情况是当 n 是素数或素数的平方时。这比原来的 O(n log n) 要好得多。返回 N=100000000 的结果需要几分之一秒。

edit:如果我们考虑附加条件,即最大素数是 13,那么 for 循环可以迭代到 13。在这种情况下,复杂度变为 O(log N),因为这是内部的复杂度循环。

【讨论】:

  • "N * (b-1) / b 没有 b 作为一个因素。" 你的意思是 N'a 减少计算。
  • 对于复杂度计算,最大素因数是13,复杂度来自while (factors_left % i == 0)所以log2(n)
  • @Jarod42 感谢您发现 `N * (b-1) / b,我现在已经解决了。
  • @Jarod42 我不明白你关于复杂度计算的评论。如果 N 的最大素数因子是某个素数 P,那么复杂度是O(min(sqrt(N), P + log(N)))。例如,如果 N=2^k,则复杂度为 O(log(N))。你是这个意思吗?
  • 对于复杂性,是的,sqrt(N) 似乎不准确,log(N) &lt; sqrt(N)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
相关资源
最近更新 更多