🧮

对数与指数:log2、log10、exp

对数 = 指数的「倒着问」——2 的几次方等于 8?exp = 复利增长的「速度表」,e 是大自然的极限增速。

💡 打个比方

乘方是「给次数,算结果」:2³ = 8。对数是「给结果,问次数」:2 的几次方等于 8?答案是 3,写作 log₂8 = 3。想象折纸:一张 8 层的纸,对折几次变成 1 层?折 3 次——「对折几次到 1」就是 log₂。计算机里 log₂ 无处不在:n 个状态要用几个 bit 区分?二分查找能把范围砍多少刀?都是它。C++ 里:log2(x) 以 2 为底、log10(x) 以 10 为底、log(x) 以 e 为底(自然对数);exp(x) = eˣ,是复利、病毒式传播背后的「增长引擎」。

术语:指数 aˣ(a 连乘 x 次);对数 logₐx(「a 的几次方等于 x」,指数的逆运算);log2(x)(以 2 为底:对折几次到 1、几个 bit);log10(x)(以 10 为底:数位数);log(x)(C++ 中是自然对数,底 e ≈ 2.71828);exp(x)(指数函数 eˣ);换底公式(log₃x = log2 x ÷ log2 3)。

🖼️ 大图看明白

左边:对折几次到 1 层?——log₂ 8 层 4 层 2 层 1 层 对折 1 次 再折 1 次 再折 1 次 8 → 4 → 2 → 1:折了 3 次 log₂8 = 3(2 的 3 次方 = 8) 右边:C++ 的「倒着问」函数 log2(8) = 3 2 的几次方 = 8?→ 3 次(8 个状态要 3 个 bit) log10(1000) = 3 1000 = 10³;位数 = 向下取整(log10 x) + 1 = 4 位 log(x) = 自然对数(底 e) log(1) = 0;log(e) = 1;log(10) ≈ 2.303(不是 1!) exp(x) = eˣ exp(1) = e ≈ 2.718;exp(2) ≈ 7.389;复利/病毒式增长 log 和 exp 互为逆运算:exp(log x) = x

🧩 子任务 1:log2(x)——对折几次、几个 bit

🎯 小目标:看懂「log₂x = 对折几次到 1」,会手推 2 的幂的 log₂,知道「n 个状态要 ⌈log₂n⌉ 个 bit」。
// 手推:折纸 + bit 对照表
  层数 8 → 4 → 2 → 1     折 3 次    log₂8 = 3
  层数 16 → 8 → 4 → 2 → 1 折 4 次    log₂16 = 4
  规律:log₂(2ⁿ) = n(2 的 n 次方,对折 n 次)

  区分 n 个状态需要几个 bit?
  2 个状态(0/1)       → 1 个 bit(2¹ = 2)
  4 个状态(0~3)       → 2 个 bit(2² = 4)
  8 个状态(0~7)       → 3 个 bit(2³ = 8)
 16 个状态(0~15)      → 4 个 bit(2⁴ = 16)
1024 个状态             → 10 个 bit(2¹⁰ = 1024)

// C++ 验证
#include <cmath>
cout << log2(8);     // 3
cout << log2(1024);  // 10
cout << log2(10);    // ≈ 3.322(10 不是 2 的幂)
✅ 小检查:① log₂32 = ?② 要区分 32 个状态需要几个 bit?③ 为什么 log₂2 = 1 而不是 2?
看看答案 ① 32 = 2⁵,log₂32 = 5。② 5 个 bit(2⁵ = 32)。③ 2¹ = 2:一张 2 层的纸对折 1 次就到 1 层;「几次方」问的是指数,2 的 1 次方就是 2 本身。
易错:log₂10 ≈ 3.322 不是整数——10 不是 2 的幂。3 个 bit 只能区分 8 个状态,要区分 10 个状态必须向上取整到 4 个 bit(16 个状态里空 6 个)。实际算「需要几个 bit」用 ⌈log₂n⌉。

🧩 子任务 2:log10 数位数 & log 是「以 e 为底」

🎯 小目标:会用「位数 = ⌊log₁₀x⌋ + 1」;记住 C++ 的 log(x) 是自然对数(底 e),不是以 10 为底。
// 手推:log10 与位数
  10   = 10¹  → log10 = 1 → 位数 = 1 + 1 = 2
 100   = 10²  → log10 = 2 → 位数 = 2 + 1 = 3
1000   = 10³  → log10 = 3 → 位数 = 3 + 1 = 4
 582385 → log10 ≈ 5.765 → 位数 = 5 + 1 = 6  ✓

// C++ 里三个「log」长这样(最容易混的地方):
#include <cmath>
log2(8);    // 3        底 2
log10(100); // 2        底 10
log(10);    // ≈ 2.303  底 e(自然对数 ln)!不是 1
// 为什么 log(10) ≈ 2.303?因为 e^2.303 ≈ 10,
// 即「e 的几次方等于 10」≈ 2.303 次
✅ 小检查:① 2026 是几位数?(用 log10 规则验证)② C++ 里 log(1) 等于多少?为什么?③ 为什么 log(100) ≈ 4.605 而不是 2?
看看答案 ① log10 2026 ≈ 3.306 → 位数 = 3 + 1 = 4 位(当然数出来也是 4 位)。② 0:e⁰ = 1,任何底的对数 log 1 都 = 0。③ 因为 C++ 的 log 底是 e:e⁴·⁶⁰⁵ ≈ 100;要底 10 必须写 log10(100) = 2。把 log 当成 log10 是新手第一大坑。

🧩 子任务 3:exp(x) 与 e——为什么增长这么猛

🎯 小目标:认识 e ≈ 2.718,会算 exp(x) = eˣ,理解复利/病毒式传播为什么是「指数」而不是「直线」。
// 复利手推:本金 100 元,年利率 100%
  一年计息 1 次:  100 × 2             = 200
  半年计息 1 次:  100 × 1.5²         = 225
  一月计息 1 次:  100 × (1+1/12)¹²   ≈ 252.7
  一天计息 1 次:  100 × (1+1/365)³⁶⁵ ≈ 271.5
  连续计息(极限):100 × e            ≈ 271.8
// 计息越来越频繁,钱不会无限多,而是停在 e 倍——e 就是「连续增长的天花板」

// 病毒式传播:每人每轮再感染 2 人
  轮次 1: 1 人 → 2: 2 人 → 3: 4 人 → 4: 8 人 → 5: 16 人
// 每轮人数 ×2(乘法增长),不是 +2(加法增长)——这就是「指数爆炸」

// C++:exp(x) = eˣ
#include <cmath>
exp(1);   // ≈ 2.71828(e 本身)
exp(2);   // ≈ 7.389
exp(10);  // ≈ 22026.5
✅ 小检查:① exp(0) = ?② exp(1) + log(e) ≈ ?③ 为什么「病毒式传播」越传越快,而不是匀速?
看看答案 ① 1:e⁰ = 1(任何数的 0 次方都是 1)。② e + 1 ≈ 3.718(log(e) = 1,因为 e¹ = e)。③ 每一轮里当前所有人都会再感染新人,新增人数和当前总数成正比(乘法),量越大涨得越快;直线增长是每轮固定 +k(加法)。复利同理:利息按当前总额算。
易错:e 不是 C++ 标准内置常量(部分编译器提供 M_E,不可依赖)。可靠写法:exp(1),或手写 2.718281828459045。同理 π 用 acos(-1.0)。

🧩 子任务 4:换底公式——没有 log3 怎么办

🎯 小目标:会推导并使用「log₃x = log₂x ÷ log₂3」(换任意底都成立),自己写出 log5 函数。
// 推导:设 log₃x = y,则 3ʸ = x
// 两边同时取 log₂:
//   log₂(3ʸ) = log₂x
//   y × log₂3 = log₂x      (指数的系数可以提到对数外面)
//   y = log₂x ÷ log₂3      ← 换底公式!

// 手推验证:log₃9 应该 = 2(因为 3² = 9)
  log₂9  ≈ 3.1699
  log₂3  ≈ 1.5849
  3.1699 ÷ 1.5849 ≈ 2.000  ✓

// C++:用 log2 实现 log3
double log3(double x) { return log2(x) / log2(3); }
log3(9);   // 2
log3(27);  // 3
log3(81);  // 4
// 换成 log10 也一样:log3(x) = log10(x) / log10(3)
✅ 小检查:① 写出 log5(x) 的函数体。② log₃27 = ?③ 为什么换底公式换成 log10 也成立(log₃x = log₁₀x ÷ log₁₀3)?
看看答案 ① return log2(x) / log2(5);(或 log10 版)。② 3(3³ = 27)。③ 推导全程只用了「a 的 b 次方的对数 = b × log a」,不依赖底是 2——换成 10(或任何 >0 且 ≠1 的底)推导照样成立,所以任意底之间都能互相换算。

💻 完整代码:密码强度估算器

// 输入:n(密码位数,每位 0~9 共 10 种选法)
// 输出:搜索空间 10ⁿ、等效 bit 数(log2)
// 示例:输入 6 → 搜索空间 1,000,000,约 19.93 bit
// 示例:输入 4 → 搜索空间 10,000,约 13.29 bit
#include <iostream>
#include <cmath>
using namespace std;

int main() {
    int n;
    cin >> n;
    double space = pow(10.0, n);     // 10ⁿ(n ≤ 9,防止整数溢出)
    double bits = log2(space);       // = n × log2(10) ≈ n × 3.322
    cout << "搜索空间: 10^" << n << " = " << (long long)space << endl;
    cout << "等效安全强度约 " << bits << " bit" << endl;
    cout << "每多 1 位数字 ≈ 多 " << log2(10.0) << " bit(乘法增长,不是 +1)" << endl;
    return 0;
}
// 动手验证:输入 1 → 10, 3.32 bit;输入 2 → 100, 6.64 bit(翻倍,不是 +3.32 的加法)

✋ 你来试试

第 1 题:4 个 bit 的二进制数能表示多少个状态?最大值是多少? 2⁴ = 16 个状态(0~15),最大值 1111₂ = 15。
第 2 题:123456789012 是几位数?用 log10 规则验证一下。 log10(123456789012) ≈ 11.09 → 位数 = ⌊11.09⌋ + 1 = 12 位(数出来也是 12 位)。
第 3 题:C++ 里 log(8) 和 log2(8) 各等于多少?(提示:log 的底是 e) log2(8) = 3(底 2);log(8) = ln 8 = 3 × ln 2 ≈ 3 × 0.693 = ≈ 2.079(底 e)。「同一个数,不同底,不同答案」。
第 4 题(加餐):复利表里「计息次数 → ∞」时结果收敛到 e,而不是无限变大——为什么? (1+1/n)ⁿ 随 n 增大单调增加,但增量越来越小,从下方逼近 e ≈ 2.71828,永远到不了 e 也超不过 3——这正是 e 的极限定义:e = lim(n→∞) (1+1/n)ⁿ。「连续增长」的天花板。
🎯 一句话记住:log 是指数的「倒着问」(log₂8 = 3);log2 管 bit 数/对折、log10 管位数(位数 = ⌊log₁₀x⌋ + 1)、C++ 的 log() 是以 e 为底的自然对数;exp(x) = eˣ,e ≈ 2.718 是复利天花板;没有 log3 就用 log₂x ÷ log₂3。