所有数字的最大公约数之和,直到n为n

时间:2015-11-06 13:44:01

标签: c++ arrays algorithm greatest-common-divisor

从1到n有n个数字。我需要找到 Σgcd(i,n)其中i = 1到i = n 对于范围10 ^ 7的n。我使用euclid的算法用于gcd,但它给了TLE。有没有找到上述总和的有效方法?

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}
int main()
{
   ll n,sum=0;
   scanf("%lld",&n);
   for(int i=1;i<=n;i++)
   {
       sum+=gcd(i,n);
   }
   printf("%lld\n",sum);
   return 0;
}

5 个答案:

答案 0 :(得分:6)

您可以通过批量GCD计算来完成此操作。 你应该找到这些除数的所有简单除数和幂。这可以在Sqtr(N)复杂度中完成。 在需要撰写GCD表之后。

可以在C#上编写代码片段,转换成C ++并不困难

int[] gcd = new int[x + 1];
for (int i = 1; i <= x; i++) gcd[i] = 1;
for (int i = 0; i < p.Length; i++)
    for (int j = 0, h = p[i]; j < c[i]; j++, h *= p[i])
        for (long k = h; k <= x; k += h)
            gcd[k] *= p[i];

long sum = 0;
for (int i = 1; i <= x; i++) sum += gcd[i];

p是这个除数的简单除数和c幂的数组。

例如,如果n = 125

  • p = [5]
  • c = [3]
  • 125 = 5 ^ 3

如果n = 12

  • p = [2,3]
  • c = [2,1]
  • 12 = 2 ^ 2 * 3 ^ 1

答案 1 :(得分:1)

我刚刚在两个数字之间实现了GCD算法,这非常简单,但我无法得到你想要做的事情。 我读到的是你试图总结一系列GCD;但是GCD是两个或多个数字之间的一系列数学运算的结果,这导致单个值。 我不是数学家,但我认为&#34; sigma&#34;正如你所写,这意味着你试图总结1到10.000.000之间数字的GCD;对我来说根本没有意义。

您试图找到GCD的价值是多少?所有数字在1到10.000.000之间?我怀疑它是不是。

无论如何,这是Euclid GCD算法的一个非常基本的(并且匆忙的)实现:

int num1=0, num2=0;
cout << "Insert the first number: ";
cin >> num1;

cout << "\n\nInsert the second number: ";
cin >> num2;
cout << "\n\n";
fflush(stdin);

while ((num1 > 0) && (num2 > 0))
{
    if ((num1 - num2) > 0)
    {
        //cout << "..case1\n";
        num1 -= num2;
    }
    else if ((num2 - num1) > 0)
    {
        //cout << "..case2\n";
        num2 -= num1;
    }
    else if (num1 = num2)
    {
        cout << ">>GCD = " << num1 << "\n\n";
        break;
    }
}

答案 2 :(得分:1)

开始查看此问题的一个好地方是在线整数序列百科全书here,因为你要做的是计算1到N之间序列A018804的总和。正如你所发现的那样尝试使用简单的Euclid GCD函数的方法太慢,因此您需要的是一种更有效的计算结果的方法。

根据OEIS链接的一个paper,可以根据欧拉函数重写总和。这将问题转变为主要因素化之一 - 仍然不容易,但可能比蛮力更快。

答案 3 :(得分:1)

我有机会研究GCD总和的计算,因为问题出现在名为GCD Sum的HackerEarth教程中。谷歌搜索结果显示some academic papers有用的公式,我在这里报告,因为在MathOverflow article由deviantfan链接时没有提及。

对于互质m和n(即gcd(m,n)== 1),函数是乘法的:

gcd_sum[m * n] = gcd_sum[m] * gcd_sum[n]

权力的权力p:

gcd_sum[p^e] = (e + 1) * p^e - e * p^(e - 1)

如果只计算一个总和,那么这些公式可以应用于对有问题的数字进行因子分解的结果,这仍然比重复的gcd()调用或通过rigmarole proposed by Толя更快。 }。

然而,公式可以很容易地用于有效地计算函数的整个表。基本上,你所要做的就是将它们插入到linear time Euler totient calculation的算法中并且你已经完成了 - 这计算所有 GCD总和比你计算的速度快一百万通过调用gcd()函数,单个GCD总数为10 ^ 6。基本上,该算法有效地枚举了最多n个数的因子分解,使得计算任何乘法函数变得容易 - Euler totient(a.k.a. phi),sigmas或实际上是GCD总和。

这里有一些ushish代码,用于计算一个GCD和的表,用于小的限制 - 在某种意义上'small',即sqrt(N)* N不会溢出32位有符号整数。 IOW,它适用于10 ^ 6的限制(足够用于HackerEarth任务,其限制为5 * 10 ^ 5)但是限制为10 ^ 7将需要在几个战略位置粘贴(long)强制转换。然而,这种在较高范围内操作的功能的强化仍然是读者的谚语......; - )

static int[] precompute_Pillai (int limit)
{
    var small_primes = new List<ushort>();
    var result = new int[1 + limit];

    result[1] = 1;

    int n = 2, small_prime_limit = (int)Math.Sqrt(limit);

    for (int half = limit / 2; n <= half; ++n)
    {
        int f_n = result[n];

        if (f_n == 0)
        {
            f_n = result[n] = 2 * n - 1;

            if (n <= small_prime_limit)
            {
                small_primes.Add((ushort)n);
            }
        }

        foreach (int prime in small_primes)
        {
            int nth_multiple = n * prime, e = 1, p = 1;  // 1e6 * 1e3 < INT_MAX

            if (nth_multiple > limit)
                break;

            if (n % prime == 0)
            {
                if (n == prime)
                {
                    f_n = 1;
                    e = 2;
                    p = prime;
                }
                else break;
            }

            for (int q; ; ++e, p = q)
            {
                result[nth_multiple] = f_n * ((e + 1) * (q = p * prime) - e * p);

                if ((nth_multiple *= prime) > limit)
                    break;
            }
        }
    }

    for ( ; n <= limit; ++n)
        if (result[n] == 0)
            result[n] = 2 * n - 1;

    return result;
}

正如所承诺的,这将在12.4毫秒内计算所有GCD总和高达500,000,而通过gcd()调用计算500,000的单个总和在同一台机器上需要48.1毫秒。代码已针对OEIS list of the Pillai function(A018804)高达2000进行验证,针对基于gcd的函数进行了高达500,000的验证 - 这项任务需要花费整整4个小时。

可以应用整个范围的优化来使代码显着更快,例如用乘法(使用反转)和比较替换模除法,或者通过步进来削减更多毫秒'prime cleaner-upper'循环模6。但是,我想以基本的,未经优化的形式显示算法,因为(a)它足够快,而且(b)它可能对其他乘法函数有用,不只是GCD总和。

PS:Granlund / Montgomery论文Division by Invariant Integers using Multiplication第9节中描述了通过与逆相乘进行的模数测试,但很难找到有效计算逆模数2的信息。大多数来源使用扩展欧几里得的算法或类似的矫枉过正。所以这里有一个函数来计算乘法逆,模2 ^ 32:

static uint ModularInverse (uint n)
{
    uint x = 2 - n;

    x *= 2 - x * n;
    x *= 2 - x * n;
    x *= 2 - x * n;
    x *= 2 - x * n;

    return x;
}

如果有人关心的话,这实际上是Newton-Raphson的五次迭代。 ; - )

答案 4 :(得分:0)

您可以使用Seive存储小于等于10 ^ 7的所有数字的最低素数因子 并且通过给定数字的素数因子化直接计算你的答案..