📌  相关文章
📜  每个数组元素的 [1, arr[i]] 范围内的非互质数对的计数(1)

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

每个数组元素的 [1, arr[i]] 范围内的非互质数对的计数介绍

问题描述

给定一个数组 arr,每个元素代表一个数值范围的上限。计算每个数值范围内非互质数对的数量。两个数是互质的,当且仅当它们的最大公约数为 1。

示例

输入: arr = [5, 10, 7]

输出: [4, 22, 12]

解释:

  • 对于 arr[0] = 5,范围为 [1,5],非互质数对有 (2,4),(2,5),(4,2),(4,5),因此计数为 4。
  • 对于 arr[1] = 10,范围为 [1,10],非互质数对有 (4,6),(4,8),(6,4),(6,8),(8,4),(8,6),(9,10),(10,9),因此计数为 22。
  • 对于 arr[2] = 7,范围为 [1,7],非互质数对有 (2,4),(2,6),(3,6),(4,2),(4,6),(6,2),因此计数为 12。
解法

我们可以从最简单的方法开始思考,一次遍历数组中每个元素的所有数值,对于其中每一对数值判断它们是否互质。这种暴力解法的时间复杂度为 $O(n^2 \times \log_2 n)$。 (其中 $n$ 表示数组长度,$\log_2 n$ 表示计算两个数的最大公约数时需要进行的二分查找)

这样的复杂度非常高,在实际应用中很难承受。因此我们需要寻找更高效的解法。

首先我们可以想到如下结论:对于每个数,保证其左侧已有的所有数与其互质,那么在其展开所有数值时,与之互质的数的数量将与所有数值的总数相同。

事实上,我们可以通过递归实现这种思路。将一个数值范围 [1,n] 分别拆分为:

  • 范围 [1,p^k],其中 p 为这个区间内的最小质因数,k 是满足 $p^k \le n$ 的最大正整数。
  • 范围 [p^k+1, n]

这样,在计算某个数值范围内的非互质数对时,我们可以先计算该区间内小于等于 p^k 的非互质数对数量,然后将剩下的范围的求解交给递归函数。

最后一个问题就是计算区间 [1,p^k] 内的非互质数对数量。对于每个质数 p,它们之间的数的最大公约数一定是 p,因此数量为 $\lfloor p^{k-1} \rfloor \times (p-1)$。

综上,我们可以得到如下的代码实现:

def count_non_coprime_pairs(n: int) -> int:
    if n == 1:
        return 0
    res = 0
    p = 2
    
    # 找到 n 范围内的最小质因数
    while p * p <= n and n % p != 0:
        p += 1
    
    # 如果 n 本身是质数,则对非互质数对数量的贡献值为 1
    if p * p > n:
        return n - 1
    
    # 计算小于等于 p^k 的非互质数对数量
    k = 0
    while n % p == 0:
        k += 1
        n //= p
    
    res += p ** (k - 1) * (p - 1)
    
    # 递归计算剩下区间内的非互质数对数量
    res += (n - 1) * count_non_coprime_pairs(p ** k)
    
    return res

def count_pairs(arr: List[int]) -> List[int]:
    res = []
    for n in arr:
        res.append(n * (n - 1) - count_non_coprime_pairs(n))
    return res

其中 count_non_coprime_pairs 函数用于计算区间 [1,n] 内的非互质数对数量,而 count_pairs 函数是问题的实际求解函数。