问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

程序员必学:快速幂算法详解

创作时间:
作者:
@小白创作中心

程序员必学:快速幂算法详解

引用
1
来源
1.
https://www.xin3721.com/Python/python19701.html

快速幂算法是计算机科学中的一个重要概念,特别是在算法竞赛和高性能计算中。本文将详细介绍快速幂算法的基本概念、实现方法以及应用场景,帮助读者深入理解这一算法的核心思想和具体实现。

关于快速幂

快速幂算法是参加算法竞赛(如 NOI、ACM 等)的必备基础知识,即使是不参加算法竞赛的程序员,也应当掌握这一算法。快速幂算法在《计算机程序设计艺术》(The Art of Computer Programming,简称 TAOCP)一书中有所提及,该书由计算机科学领域的知名科学家 Donald Ervin Knuth 编写。

Knuth 在 1974 年获得图灵奖,目前 TAOCP 已出版了第 1、2、3、4A 卷,计划中的第 4B、5、6、7 卷尚未出版。第一卷首发于 1968 年,Knuth 现年 82 岁,计划在 105 岁之前完成这部巨著。

微软创始人 Bill Gates 曾评价道:“如果你认为自己是一位非常优秀的程序员,那就应该阅读 Knuth 的 TAOCP;如果你能读懂全部内容,可以直接给我发送一份简历。” Knuth 本人则表示:“看不懂就别当程序员了!”不过 TAOCP 对新手来说阅读难度较大,书中的所有示例都使用了 Knuth 自创的 MIX 汇编语言。

阅读本文之前的提醒

在深入学习快速幂算法之前,需要具备以下基础知识:

  • 熟悉算法中的时间复杂度和空间复杂度概念
  • 熟悉二进制和十进制的转换
  • 熟悉常见的位运算操作,如 n & 1 用于获取最低二进制位的值,n >> 1 用于求正整数 n 的一半

什么是幂(Power)?

幂运算的基本概念是:xn 次幂表示 nx 相乘,例如 2 的 4 次幂就是 2 * 2 * 2 * 2。用简化表示法,xn 次幂记作 x ^ n(本文中的 ^ 不表示按位异或)。

最直观的幂运算实现方法如下(以 Java 语言为例):

int power(int x, int n) {
    int result = 1;
    while (n-- > 0) {
        result *= x;
    }
    return result;
}

这种方法的时间复杂度是 O(n),空间复杂度是 O(1)。

什么是快速幂?

快速幂算法的目标是提高幂运算的效率,将时间复杂度优化至 O(logn)。这里介绍两种实现方法:递归和非递归。

递归实现

递归实现的快速幂算法代码如下:

int fastPower(int x, int n) {
    if (n == 0) return 1;
    int result = fastPower(x, n >> 1);
    result *= result;
    return (n & 1) == 0 ? result : result * x;
}

这个方法的时间复杂度和空间复杂度都是 O(logn)。分析复杂度的方法包括:

  • 代入特定值进行分析,例如当 n 为 16 时,方法的递归调用过程可以直观地展示每次调用时 n 的规模减半的特点。
  • 使用主定理(Master Theorem)进行更专业的复杂度分析。

非递归实现

以计算 3 的 21 次幂为例,分析非递归实现的代码:

int fastPower(int x, int n) {
    int result = 1;
    while (n != 0) {
        if ((n & 1) == 1) {
            result *= x;
        }
        x *= x;
        n >>= 1;
    }
    return result;
}

执行流程如下:

  1. 初始化 result 为 1。
  2. n 不为 0 时,进入循环:
  • 检查 n 的最低位是否为 1,如果是,则将当前的 x 乘入 result
  • x 自身平方(相当于计算下一个 2 的幂次)。
  • n 右移一位(相当于检查下一个二进制位)。

这种方法的时间复杂度是 O(logn),空间复杂度是 O(1)。

LeetCode 实战

LeetCode 上的第 50 号题 "Pow(x, n)" 可以直接应用快速幂算法。以下是两种实现方式:

递归实现

public double myPow(double x, int n) {
    if (n == 0) return 1;
    if (n == -1) return 1 / x;
    double half = myPow(x, n >> 1);
    half *= half;
    return ((n & 1) == 1) ? half * x : half;
}

非递归实现

public double myPow(double x, int n) {
    long y = (n < 0) ? -((long) n) : n;
    double result = 1.0;
    while (y > 0) {
        if ((y & 1) == 1) {
            result *= x;
        }
        x *= x;
        y >>= 1;
    }
    return (n < 0) ? (1 / result) : result;
}

这两种实现方式都充分利用了快速幂算法的优势,能够高效地处理大数的幂运算问题。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号