幂与对数
幂
幂是指将一个数(底数)自乘若干次(由指数决定)的运算。其一般形式为 ,表示 乘以自身 次。
如中,是底数,是指数。
不同指数的幂有特性:
正整数指数
- 域: 1
- 定义:
- 性质:
零指数
- 域:
- 定义:
- 注:
- 这是为了保持幂运算的连续性,例: ,确保像这类运算可以正常执行。
- 未定义的。(某些情况下等于一但不推荐使用)
负整数指数
- 域:
- 定义:
- 注:
- 负指数表示倒数。
- 这是为了扩展幂运算的定义域,使其在整数范围内封闭。
分数指数
- 域:
- 定义:
注: 分数指数的本质是将幂运算延拓到有理数集。这里的推广基于正整数指数的性质: :
如果分数指数要满足该性质,我们可以用一个简单的式子: 看前面的一项 再利用,不难得到。
无理指数
- 域:
- 注: 无理指数将幂运算延拓到实数甚至复数集。
定义: 设 是一个正实数, 是一个无理数。选取一个有理数序列 使得 ,则无理指数 定义为: 这个方法的本质是通过有理数无限逼近一个无理数来进行。我们也可以通过指数函数来进行延拓,此处不再赘述。
编程中使用
一般来讲,我们将设计的幂算法只支持自然数。更广的作用域,在编程中通常不使用。接下来我们开始设计:
从定义出发,我们不难想到只需要对一个基数进行循环n
次,每次乘它自身,便可得到:
pub fn power_iter(base: i64, exponent: u32) -> i64 {
// 下面省略溢出检测
let mut result = 1;
for _ in 0..exponent {
result *= base;
}
result
}
它的时间复杂度是,对于较大的输入规模,这个算法可能并不特别好。我们还有一种名为快速幂的方法:
fn power_fast(mut base: i64, mut exponent: u32) -> i64 {
let mut result = 1;
while exponent > 0 {
if exponent % 2 == 1 {
result *= base;
}
base *= base;
exponent /= 2;
}
result
}
它采用分治法的策略,时间复杂度是。其数学基础是对于输入规模,如果它是偶数,那么,如果它是奇数,那么。快速幂算法实际上是在利用指数的二进制表示: 2
所以完全可以用位运算:
fn power_fast(mut base: i64, mut exponent: u32) -> i64 {
let mut result = 1;
while exponent > 0 {
if exponent & 1 == 1 { // 等价于 exponent % 2 == 1
result *= base;
}
base *= base;
exponent >>= 1; // 等价于 exponent /= 2
}
result
}
let base = 2;
let exponent = 3;
let result = base.pow(exponent); // 2^3 = 8
对数
对数(Logarithm) 是数学中用于简化乘除运算的一种函数,定义为幂运算的逆运算。具体来说,如果满足以下等式: 那就是以为底的对数,记作: 其中,为底数(),为真数()。在上面这两个式子中,我们可以得到对数与指数的关系: 我们常用几种特殊的对数:
- : 常用对数。(可以简写为),用于科学计算和工程。
- : 自然对数。(可以简写为),广泛用于微积分和自然科学。
- : 二进制对数。(仅在计算机科学中,我们可以简写为),常见于计算机科学和信息论。
性质
对数的运算有很多特殊的性质:
乘法性质
性质:
证明:
设 ,,则:
两式相乘:
取对数得:
证毕。
除法性质
性质:
证明:
设 ,,则:
两式相除:
取对数得:
证毕。
幂运算性质
性质:
证明:
设 ,则:
取对数得:
证毕。
换底公式
性质:
证明:
设 ,则:
解得:
即:
证毕。
特殊值性质
性质1:
证明:
由 ,根据定义:
证毕。
性质2:
证明:
由 ,根据定义:
证毕。
对数将复杂的乘除、幂运算转化为加减乘除,是数学和科学中不可或缺的工具。
在编程中使用
其实在幂那里我们就已经知道了——由于计算机的二进制特性,利用位运算我们可以计算整数以为底的对数:
pub fn log2_bitwise(n: u32) -> u32 {
// 下面省略数据校验
31 - n.leading_zeros()
}
位操作法 的核心思想是一个整数的二进制表示中,最高有效位(MSB)的位置即为其以2为底的对数。n.leading_zeros()
返回n
的二进制表示中前导零3的个数。u32
的二进制长度是32
,由于位置编号从0
开始(范围是0
~31
),所以最高有效位是31 - leading_zeros
。对于其他底数,我们也可以使用牛顿迭代法:
pub fn log_newton(x: f64, base: f64, epsilon: f64) -> f64 {
// 下面省略数据校验
let mut y = (x.ln() / base.ln()).max(0.0); // 初始猜测
loop {
let next_y = y - (base.powf(y) - x) / (base.powf(y) * base.ln());
if (next_y - y).abs() < epsilon {
return next_y;
}
y = next_y;
}
}
牛顿迭代法 是一种用于近似求解方程 的根的高效数值方法。其主要通过切线逼近逐步修正解的近似值。从一个初始猜测值开始。用当前点的切线(导数)更新: 重复迭代,直到小于预设精度4。
// log
let five = 5.0f32;
// log5(5) - 1 == 0
let abs_difference = (five.log(5.0) - 1.0).abs();
// log2
let two = 2.0f32;
// log2(2) - 1 == 0
let abs_difference = (two.log2() - 1.0).abs();
// log10
let ten = 10.0f64;
// log10(10) - 1 == 0
let abs_difference = (ten.log10() - 1.0).abs();
// ln
let one = 1.0_f64;
// e^1
let e = one.exp();
// ln(e) - 1 == 0
let abs_difference = (e.ln() - 1.0).abs();
练习与回答
- 试证明一个整数的二进制表示中,最高有效位(MSB)的位置即为其以2为底的对数。
证明
命题:对于正整数,其二进制最高有效位的位置满足:
证明:
设的二进制表示为:
因为,所以:
对不等式取:
因此: