📜  计算所有子阵列上的GCD之和(1)

📅  最后修改于: 2023-12-03 15:28:01.877000             🧑  作者: Mango

计算所有子阵列上的GCD之和

本文介绍一种高效的算法来计算一个数组的所有子阵列上的最大公约数(GCD)之和。这个算法的时间复杂度为O(nlogn),其中n是数组的长度。

算法描述

我们考虑一个长度为n的数组A,GCD(A[i, j])表示从i到j的子阵列的最大公约数,sumGCD表示所有子阵列上GCD之和。

我们开始从左往右遍历数组,假设当前位置是k。那么,对于任意的i(0≤i≤k),GCD(A[i,k])都可以被计算出来。这是因为GCD(A[i,k])的值只需要考虑前一项GCD(A[i,k-1])和当前项A[k]的因数集合即可。我们可以通过Euclid算法,在O(logn)的时间内计算两个数的最大公约数。

在遍历到k时,我们可以将所有在i≤k时已经计算出GCD的子阵列数量统计起来,将这个数量乘以A[k]的因数数量,然后加到sumGCD中。这个因数数量可以通过使用线性筛法来计算,在O(nloglogn)的时间内完成。

具体步骤如下:

  1. 令sumGCD = 0。
  2. 遍历数组A,假设当前位置是k。
  3. 使用Euclid算法计算GCD(A[i,k]),其中0≤i≤k。
  4. 使用线性筛法计算当前项A[k]的因数数量。
  5. 令cnt表示当前已经计算出GCD的子阵列数量,初始值为0。
  6. 将所有在i≤k时已经计算出GCD的子阵列数量统计起来,加到cnt中。也就是说,cnt += k-i+1。
  7. 将cnt乘以A[k]的因数数量,加到sumGCD中。
  8. 返回sumGCD。

以下是代码片段:

def sumGCD(A):
    n = len(A)
    primes = []  # 存储质数集合
    mu = [0] * (n+1)  # 筛法中的mobius函数
    isp = [True] * (n+1)  # 标记某数是否为质数

    mu[1] = 1  # 初始化
    for i in range(2, n+1):
        if isp[i]:
            primes.append(i)
            mu[i] = -1
        
        for p in primes:
            if i * p > n:
                break
            isp[i * p] = False
            
            if i % p == 0:
                mu[i * p] = 0
                break
            else:
                mu[i * p] = -mu[i]

    sumGCD = 0
    cnt = 0
    gcds = []  # 存储所有GCD(A[i,k])
    for k in range(n):
        for i in range(k+1):
            if i == 0:
                gcds.append(A[k])
            else:
                gcds[i-1] = gcd(gcds[i-1], A[k])
            cnt += 1
        fs = getFactors(A[k], primes)  # 计算A[k]的因数数量
        for f in fs:
            sumGCD += f * mu[cnt]
        cnt = 0

    return sumGCD
性能分析

本算法的时间复杂度为O(nlogn),其中n是数组的长度。具体来说,线性筛法需要O(nloglogn)的时间,Euclid算法需要O(logn)的时间,子阵列数量的统计需要O(n),因数数量计算需要O(logA[k]),因此总时间复杂度为O(nlogn)。

此外,本算法的空间复杂度为O(n),主要用于存储所有GCD(A[i,k])和线性筛法中的数据结构。

参考文献