直到n的所有数的最大公约数与n的和

Sum of Greatest Common Divisor of all numbers till n with n

本文关键字:的和 最大公约数 直到      更新时间:2023-10-16

从1到n有n个数字。我需要找到∑gcd(i,n),其中i=1至i=n对于10^7范围内的n。我对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("%lldn",sum);
   return 0;
}

您可以通过批量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

我刚刚实现了两个数字之间的GCD算法,这很容易,但我无法理解您在那里要做的事情。我在那里读到的是,你试图总结一系列GCD;但GCD是两个或多个数字之间一系列数学运算的结果,这些运算产生一个值。我不是数学家,但我认为你写的"西格玛"意味着你试图求出1到10.000.000之间的数字的GCD;这对我来说毫无意义。

你试图找到GCD的价值观是什么?所有介于1和10.000.000之间的数字?我怀疑是这样。

不管怎样,这里有一个非常基本(而且很匆忙)的欧几里得GCD算法的实现:

int num1=0, num2=0;
cout << "Insert the first number: ";
cin >> num1;
cout << "nnInsert the second number: ";
cin >> num2;
cout << "nn";
fflush(stdin);
while ((num1 > 0) && (num2 > 0))
{
    if ((num1 - num2) > 0)
    {
        //cout << "..case1n";
        num1 -= num2;
    }
    else if ((num2 - num1) > 0)
    {
        //cout << "..case2n";
        num2 -= num1;
    }
    else if (num1 = num2)
    {
        cout << ">>GCD = " << num1 << "nn";
        break;
    }
}

从整数序列在线百科全书开始研究这个问题的一个好地方是,你要做的是计算1和N之间的序列A018804的和。正如你发现的那样,尝试使用简单欧几里得GCD函数的方法太慢了,所以你需要一种更有效的方法来计算结果。

根据OEIS的一篇论文,可以用欧拉函数重写和。这将问题转化为素数分解——仍然不容易,但可能比暴力快得多。

我有机会研究GCD求和的计算,因为这个问题出现在一个名为GCD Sum的HackerArth教程中。谷歌搜索发现了一些带有有用公式的学术论文,我在这里报道这些公式,因为在由deviationfan链接的MathOverflow文章中没有提到它们。

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

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

素数p的幂e:

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

如果只计算一个和,那么这些公式可以应用于对所讨论的数字进行因子分解的结果,这仍然比重复的gcd()调用或通过Т。

然而,这些公式同样可以很容易地用于有效地计算函数的整个表。基本上,你所要做的就是将它们插入线性时间Euler瞬态计算的算法中,你就完成了——这计算所有GCD总和的速度比你通过调用gcd()函数计算数字10^6的单个GCD总和快得多。基本上,该算法有效地枚举了n以下数字的最小因子分解,使计算任何乘法函数变得容易——Euler totient(又名phi)、sigmas,或者实际上是GCD和。

这里有一个散列代码,它计算了一个小极限的GCD和表——"小"是指sqrt(N)*N不会溢出32位有符号整数。IOW,它的限制为10^6(对于限制为5*10^5的HackerArth任务来说足够了),但限制为10^2需要在几个战略位置粘贴(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毫秒内计算出高达500000的所有GCD总和,而在同一台机器上,通过gcd()调用计算500000的单个总和需要48.1毫秒。该代码已根据Pillai函数(A018804)的OEIS列表进行了验证,直到2000年,并根据基于gcd的函数进行了高达500000的验证,这项工作花了整整4个小时。

有一系列的优化可以用来使代码明显更快,比如用乘法(用倒数)和比较代替模除法,或者通过步进"素数清理器上"循环模6来节省更多毫秒。然而,我想以其基本的、未优化的形式展示该算法,因为(a)它非常快,(b)它可能对其他乘法函数有用,而不仅仅是GCD和。

附言:Granlund/Montgomery论文《用乘法除以不变整数》的第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;
}

如果有人关心的话,这实际上是牛顿-拉斐森的五次迭代

您可以使用Seive存储所有小于10^7的数字的最低素数并且给定数的by by素数分解直接计算你的答案。。