🚀

快速幂:倍增法

算 2 的 100 次方,逐个乘要 99 次,快速幂只要 7 轮——因为每轮「平方一下」,幂次就翻倍!

💡 打个比方

想象一个翻倍的跳远:你一次只能跳 1 米,但每跳完一次,下一次的跳远距离可以翻倍:1 米 → 2 米 → 4 米 → 8 米 → 16 米……要覆盖 13 米怎么办?别傻跳 13 次:取 8 + 4 + 1 = 13——跳 3 次「翻倍」得到 8 米和 4 米的能力,再拼上起步的 1 米,搞定。

算幂次也是同一招!算 2¹³:别连乘 12 次,先「平方」得到 2² → 2⁴ → 2⁸(只平方 3 次),再用 13 = 8 + 4 + 1 拼起来:2¹³ = 2⁸ × 2⁴ × 2¹。每一步平方都把「能表示的指数」翻倍,所以指数再大,轮数也只是指数的二进制位数——这就是倍增法。

两条基本规律(后面全靠它们):a^(x+y) = a^x × a^y(指数相加 → 幂相乘);a^(2x) = (a^x)²(指数翻倍 → 平方一下就行)。

术语:快速幂(Fast Power:用「平方 + 按二进制位乘」把 aⁿ 从 O(n) 次乘法降到 O(log n) 轮);倍增法(每次让「能用的能力」翻倍:2¹→2²→2⁴→2⁸,拼出任意指数);指数二进制(13 = 1101₂ = 8+4+1,哪一位是 1 就乘进哪个平方结果);O(log n)(每轮指数减半,n 再大轮数也只是 log₂ n 量级)。

🖼️ 大图看明白

平方 平方 平方 13 = 8 + 4 + 1(二进制 1101) 2¹³ = 2⁸ × 2⁴ × 2¹ 只平方 3 次(2¹→2²→2⁴→2⁸),不用连乘 12 次 粉色 = 13 的二进制里「是 1」的位;灰色的 2² 用不上 逐个乘:12 次乘法 快速幂:4 轮(每轮 = 平方 + 位是 1 才乘) 轮 1 轮 2 轮 3 轮 4 2¹⁰⁰:99 次 → 7 轮(100 = 1100100₂,7 位) 2¹⁰⁸:27 轮(10⁸ < 2²⁷ = 134217728) O(n) → O(log n):指数每轮减半, 轮数 = 指数的二进制位数 翻倍链:每平方一次,能用的指数翻倍 算 2¹³ 要几步? 2¹ 2² 2⁴ 2⁸

🧩 子任务 1:问题——逐个乘有多慢

🎯 小目标:看清「a^b 逐个乘要 b−1 次乘法」,体会指数很大时(10⁸)逐个乘为什么来不及。

算 2¹⁰⁰,最直白的办法是连乘:2 × 2 × 2 × … × 2(100 个 2),要做 99 次乘法。指数是 b,就要 b−1 次乘法——这叫 O(n)(线性:指数多大,步数就多大)。

// 逐个乘(朴素算法):每步乘一次底数
2^1  = 2
2^2  = 2 × 2        // 1 次乘法
2^3  = 2 × 2 × 2    // 2 次乘法
2^10 = 2 连乘 9 次   // 9 次乘法
2^100 = 2 连乘 99 次 // 99 次乘法——还行

// 但指数是 10^8(一亿)时呢?
2^(10^8) = 2 连乘 99999999 次  // 一亿次!每秒 10^8 次也要 1 秒,
                                // 慢一点的机器就是「卡死」

// 目标:把 b−1 次乘法,降到「log₂ b 量级」的轮数
✅ 小检查:① 算 3²⁰(逐个乘)要几次乘法?② 指数 b = 1000 时逐个乘要几次?③ 「O(n)」在这里指什么和什么成正比?
看看答案 ① 3²⁰ 要 19 次乘法(b−1 = 19)。② 999 次。③ 步数 ∝ 指数:指数 b 有多大,乘法次数就有多大(b−1 次)——这就是线性 O(n),n 就是指数。
易错:以为「答案很大」所以慢——慢是因为乘法次数多,不是因为答案大。2¹⁰⁰ 的答案约 3.16 × 10³⁰(超过 int,要用 long long 或更宽),但就算答案装得下,99 次乘法也是 99 次。

🧩 子任务 2:倍增思想——平方一次,指数翻倍

🎯 小目标:会用两条规律 a^(x+y) = a^x × a^y 和 a^(2x) = (a^x)²,把大指数「拆成 2 的幂再拼回去」。

关键观察:平方一下,指数就翻倍。2² 平方一下 = (2²)² = 2⁴;2⁴ 平方一下 = 2⁸;2⁸ 平方一下 = 2¹⁶……所以「2 的 2 的幂次方」可以链式免费得到:

// 翻倍链:每步一次平方,指数 ×2
2^1 = 2
2^2 = (2^1)² = 4        // 1 次平方
2^4 = (2^2)² = 16       // 第 2 次平方
2^8 = (2^4)² = 256      // 第 3 次平方
2^16 = (2^8)² = 65536   // 第 4 次平方

// 算 2^13?13 不是 2 的幂,但 13 = 8 + 4 + 1:
2^13 = 2^(8+4+1) = 2^8 × 2^4 × 2^1 = 256 × 16 × 2 = 8192
// 只用:3 次平方 + 2 次「拼」乘法 = 5 次(逐个乘要 12 次)

// 更大的:2^100 = (2^50)² = ((2^25)²)² …… 指数 100 → 50 → 25 → 13 → 7 → 4 → 2 → 1
// 反过来读:从 2^1 起步,平方 7 次就能「够到」2^100 的规模,
// 再按 100 = 64 + 32 + 4(二进制 1100100)把对应的几块乘起来。

// 为什么拆成 8 + 4 + 1?因为 13 的二进制是 1101:
//   13 = 1×8 + 1×4 + 0×2 + 1×1 → 哪一位是 1,就乘哪一块
✅ 小检查:① 2⁵ = ?用翻倍链怎么拼(5 = 4 + 1)?② 2⁶ 怎么拼(6 = 4 + 2)?需要几块?③ 2⁷ = 128,用翻倍链要「拼」几次乘法?
看看答案 ① 2⁵ = 2⁴ × 2¹ = 16 × 2 = 32(2 块,1 次拼乘法 + 2 次平方)。② 2⁶ = 2⁴ × 2² = 16 × 4 = 64(2 块,1 次拼)。③ 7 = 4 + 2 + 1 → 2⁷ = 16 × 4 × 2,要 2 次拼乘法(3 块乘一起 = 2 次 ×)。规律:拼的乘法次数 = 二进制里 1 的个数 − 1。
易错:把 13 拆成「8 + 5」再拆「5 = 4 + 1」——虽然结果对,但只拆成 2 的幂(8/4/2/1)才能直接复用翻倍链。二进制就是「2 的幂的加法」:13 = 1101₂ = 8 + 4 + 1。

🧩 子任务 3:快速幂算法——手推 2¹³ 全程

🎯 小目标:记住三步「位是 1 就乘、平方、指数减半」,能手推 2¹³ = 8192 的全过程,并数出轮数。

把「翻倍链 + 按位拼」写成固定动作,每轮做三件事:① 指数的最低位是 1?是 → 结果 ×= 底数;② 底数平方;③ 指数 ÷ 2(取整)。指数变 0 就停。

// 快速幂(迭代版)伪代码:算 a^b
res = 1            // 结果,先放 1(乘法的起点)
while b > 0:
    若 b 是奇数: res = res × a    // 最低位是 1 → 乘进当前这块
    a = a × a                     // 底数平方 → 下一块指数翻倍
    b = b ÷ 2(取整)             // 指数右移一位
return res

// 手推 2^13(13 的二进制 = 1101,从低位看起:1、0、1、1)
轮  b(本轮开始)  奇?  本轮后 res      平方后 a
0   13           是    1×2 = 2        2 → 4
1   6            否    2              4 → 16
2   3            是    2×16 = 32      16 → 256
3   1            是    32×256 = 8192  256 → 65536
b = 0,停 → 2^13 = 8192 ✓(2^13 = 8192 验证过)

// 一共 4 轮,乘法共 7 次:4 次「平方」+ 3 次「乘进 res」(第 0、2、3 轮)
// 逐个乘要 12 次 → 指数越大,优势越明显!

// 对照二进制:13 = 1101,最低位起依次是 1、0、1、1
// 位是 1 的轮(第 0、2、3 轮)各乘进一块:2^1、2^4、2^8
// res = 2^1 × 2^4 × 2^8 = 2^13 —— 正是 13 = 1 + 4 + 8
✅ 小检查:① 手推 3⁵:列出每轮的 res 和 a(5 = 101₂)。② 为什么「b 是奇数」才乘进 res?③ 为什么每轮结尾 b 要 ÷ 2?
看看答案 ① 3⁵:轮 0:b=5 奇 → res=3,a=9,b=2;轮 1:b=2 偶 → res=3,a=81,b=1;轮 2:b=1 奇 → res=3×81=243,a=6561,b=0 → 243 ✓(3⁵ = 243)。② b 的最低位是 1,才说明「指数的二进制里这一位要算」,乘进的是当前这块(a、a²、a⁴、a⁸……,每轮平方一次);位是 0 就不需要这块。③ ÷ 2 就是把指数的最低位「用掉」,下一轮处理下一位——每轮处理一位,所以轮数 = 位数。
易错:把「先平方再判断」写反——顺序必须是先判断最低位(该乘则乘),再平方、再减半。第 0 轮的 a 就是 a¹,如果先平方,第 0 轮乘进的就变成 a² 了,整个答案错 2 倍。

🧩 子任务 4:复杂度 O(log n)——指数每轮减半

🎯 小目标:明白「轮数 = 指数的二进制位数 ≈ log₂ b」,会比较 O(n) 和 O(log n) 在 b = 10⁸ 时的差距。

每轮 b ÷ 2,b 从「一亿」变「一」要多少次?就是二进制位数:

// b 每轮减半:轮数 = 把 b 写成二进制的位数
b = 13  → 1101₂       4 位 → 4 轮(子任务 3 数过 ✓)
b = 100 → 1100100₂    7 位 → 7 轮(log₂100 ≈ 6.64,进一 7)
b = 10^8 → 二进制 27 位 → 27 轮
        (2^26 = 67108864 < 10^8 < 2^27 = 134217728)

// 对比(b = 10^8):
逐个乘:99999999 次乘法        // O(n):一亿次
快速幂:27 轮(每轮 1 次平方 + 至多 1 次乘) // O(log n):27 轮!

// 「log」的直觉:10^8 看着很大,但「减半次数」很小——
// 1亿 → 5千万 → 2500万 → … → 1,数一数:27 步就到底
// 所以指数再大(10^18?),快速幂也只要 ~60 轮
✅ 小检查:① b = 1000 时快速幂多少轮?(提示:2¹⁰ = 1024)② 为什么 2⁶⁰ 用 long long 装得下?(提示:2⁶³ ≈ 9.2 × 10¹⁸ = long long 上限量级)③ 快速幂每轮最多做几次乘法?
看看答案 ① 1000 < 2¹⁰ = 1024,二进制 10 位(1111101000)→ 10 轮(逐个乘要 999 次)。② 2⁶³−1 = 9223372036854775807 正是 long long 的最大值,所以 2⁶⁰ ≈ 1.15 × 10¹⁸ 装得下,而 2⁶⁴ 就溢出了。③ 每轮最多 2 次:1 次「平方」+ 位是 1 时的 1 次「乘进 res」;位是 0 时只有 1 次平方。
易错:说「快速幂是 O(log n)」却写不出依据——要说清每轮 b ÷ 2,所以轮数 = b 的二进制位数 = ⌊log₂ b⌋ + 1。「指数减半」是 O(log n) 的根源,不是「乘法变少了」。

💻 完整代码:快速幂 fastpow(a,b) + 轮数对比

// 输入:a b(2 ≤ a ≤ 10,0 ≤ b ≤ 60,答案用 long long 防溢出)
// 输出:a^b(快速幂结果)+ 快速幂轮数 vs 逐个乘次数
// 示例:2 13 → 8192(4 轮 vs 12 次)
//       3 5 → 243(3 轮 vs 4 次)
//       2 0 → 1(0 轮 vs 0 次)
#include <iostream>
using namespace std;

long long fastpow(long long a, int b) {
    long long res = 1;          // 结果从 1 开始(乘法的起点)
    while (b > 0) {
        if (b % 2 == 1)          // 最低位是 1:乘进当前这块
            res *= a;
        a *= a;                  // 平方:下一块指数翻倍
        b /= 2;                  // 指数减半(右移一位)
    }
    return res;
}

int main() {
    long long a;
    int b;
    cin >> a >> b;

    int rounds = 0;              // 快速幂轮数 = b 的二进制位数
    for (int t = b; t > 0; t /= 2)
        rounds++;

    cout << a << "^" << b << " = " << fastpow(a, b) << endl;
    cout << "快速幂 " << rounds << " 轮,逐个乘 " << b << " 次乘法" << endl;
    return 0;
}
// 动手验证:输入 2 13 → 8192(4 轮 vs 12 次)
// 输入 7 20 → 79792266297612001(5 轮 vs 19 次)
// 输入 10 18 → 1000000000000000000(5 轮 vs 17 次)

✋ 你来试试

第 1 题:用快速幂手推 2²⁰,列出每轮(20 = 10100₂)。 轮 0:b=20 偶 → res=1,a=4,b=10;轮 1:b=10 偶 → res=1,a=16,b=5;轮 2:b=5 奇 → res=16,a=256,b=2;轮 3:b=2 偶 → res=16,a=65536,b=1;轮 4:b=1 奇 → res=16×65536=1048576,a=…,b=0。验证:2²⁰ = 1048576 ✓。20 = 16 + 4,res = 2⁴ × 2¹⁶。
第 2 题:3¹⁰ = ?(快速幂或手算) 3¹⁰ = (3⁵)² = 243² = 59049。快速幂:10 = 1010₂ → res = 3² × 3⁸ = 9 × 6561 = 59049 ✓。
第 3 题:算 7¹⁰⁰,快速幂要几轮?逐个乘要几次? 100 = 1100100₂,7 位 → 快速幂 7 轮;逐个乘要 99 次。每轮至多 2 次乘法,总共 ≤ 14 次——差距一目了然。
第 4 题(加餐):为什么 fastpow 里 res 要从 1 开始,而不是 0? res 是「拼起来的乘积」:乘法的单位元是 1(1 × x = x),从 0 开始,0 × 任何数都还是 0,最后永远是 0。这和「加法从 0 开始、乘法从 1 开始」是同一个道理。
🎯 一句话记住:快速幂 = 倍增法:每轮「位是 1 才乘、平方、指数减半」;轮数 = 指数的二进制位数,O(n) → O(log n)——指数 10⁸ 也只要 27 轮。