返回

阶乘的本质与奥秘:揭秘LeetCode 793. 阶乘函数后 K 个零(困难)

后端

计算阶乘函数末尾零的个数:一个彻底的指南

概述

阶乘函数,表示为 f(x) = x!,是一个基本的数学概念,经常出现在各种数学和计算机科学问题中。然而,当我们处理非常大的数字时,理解阶乘函数的本质和末尾零的由来至关重要。

阶乘函数的本质

阶乘函数的定义很简单:它将从 1 到给定数字 x 的所有正整数相乘。例如,5! = 1 * 2 * 3 * 4 * 5 = 120。

末尾零的由来

阶乘函数末尾零的个数取决于两个关键因素:质因数 2 和 5。

  • 质因数 2: 当 x 可以被 2 整除时,阶乘函数中就会有一个质因数 2。例如,10! 中有一个质因数 2,因为 10 可以被 2 整除。
  • 质因数 5: 类似地,当 x 可以被 5 整除时,阶乘函数中就会有一个质因数 5。例如,25! 中有一个质因数 5,因为 25 可以被 5 整除。

算法详解

理解了阶乘函数的本质和末尾零的来源后,我们可以设计一个高效的算法来计算阶乘函数末尾零的个数:

步骤 1:计算 x 的二进制表示中 1 的个数 (m)

这是因为每个 1 代表一个质因数 2。例如,10 的二进制表示为 1010,其中有 2 个 1,这意味着 10! 中有 2 个质因数 2。

步骤 2:计算 x 可以表示为 5 的最大幂次 (p)

这是因为每个 5 的幂次都会贡献一个质因数 5。例如,25 可以表示为 5²,这意味着 25! 中有 2 个质因数 5。

步骤 3:返回 m 和 p 中较小的一个

这是因为末尾零的个数由较少的质因数 2 或 5 决定。例如,如果 m = 3 和 p = 5,那么末尾零的个数为 min(3, 5) = 3。

代码示例

def trailing_zeroes(x):
    """
    计算阶乘函数 f(x) = x! 末尾零的个数。

    Args:
        x: 给定的整数

    Returns:
        末尾零的个数
    """
    m = bin(x).count('1')  # 计算质因数 2 的个数
    p = 0
    while x >= 5**p:
        p += 1  # 计算质因数 5 的个数
    return min(m, p)

print(trailing_zeroes(5))  # 输出:1
print(trailing_zeroes(11))  # 输出:2
print(trailing_zeroes(100))  # 输出:24

复杂度分析

  • 时间复杂度:O(log x),其中 x 是给定的整数。这是因为算法需要 O(log x) 时间来计算二进制表示和 5 的幂次。
  • 空间复杂度:O(1),算法不需要额外的空间。

结论

理解阶乘函数末尾零的来源对于解决各种数学和计算机科学问题至关重要。通过采用分步算法,我们可以高效地计算阶乘函数末尾零的个数。

常见问题解答

  1. 为什么质因数 2 和 5 在计算末尾零时很重要?
    质因数 2 和 5 是影响阶乘函数末尾零的主要因素。当 x 可以被 2 或 5 整除时,它会产生额外的质因数 2 或 5,从而导致末尾出现零。
  2. 算法中计算二进制表示中 1 的个数有什么用?
    计算二进制表示中 1 的个数可以确定质因数 2 的个数,因为每个 1 都表示一个 2 的幂次。
  3. 算法中计算 x 可以表示为 5 的最大幂次有什么用?
    计算 x 可以表示为 5 的最大幂次可以确定质因数 5 的个数,因为每个 5 的幂次都会产生一个 5 的质因数。
  4. 算法中返回 m 和 p 中较小的一个有什么意义?
    末尾零的个数由较少的质因数 2 或 5 决定,因此返回较小的一个可以确保准确计算。
  5. 这个算法的复杂度是多少?
    算法的时间复杂度为 O(log x),其中 x 是给定的整数,空间复杂度为 O(1)。