ruby 实现快速幂
快速幂算法(Exponentiation by Squaring) 的 Ruby 实现。
快速幂的核心思想:
-
如果 \(n\) 是偶数:
\[a^n = (a^2)^{n/2} \] -
如果 \(n\) 是奇数:
\[a^n = a \times (a^2)^{\lfloor n/2 \rfloor} \] -
时间复杂度: \(O(\log n)\)。
一、递归实现
def fast_pow(base, exp)
return 1 if exp == 0
return base if exp == 1
if exp.even?
half = fast_pow(base, exp / 2)
half * half
else
base * fast_pow(base, exp - 1)
end
end
puts fast_pow(2, 10) # => 1024
puts fast_pow(3, 5) # => 243
二、迭代实现(推荐)
迭代版通常比递归快,也避免栈溢出,思路是:每次都把底数 base 翻倍,指数减半,但当指数是奇数时,少算一个底数,故用 if 语句提前判断一下,是奇数就多乘一个底数。
比如 $ b^{16} $ 由 $ b $, $ b^2 $, $ b^4 $, $ b^8 $, $ b^{16} $, 对应指数 e 为 $ 16, 8, 4, 2, 1 $ ,注意,这里对应的是循环中 result参与的 b, 因为这时的 b 才是我们需要的b,用来计算最终result的结果。
这种算式,其实很难在大脑中连续的去思考,真正思考时,是跳跃式思考,根据公式的正确性,直接得出答案,而不是在具体的数字上在连续的思考具体的结果。
def fast_pow(base, exp)
result = 1
b = base
e = exp
while e > 0
result *= b if e.odd?
b *= b
e /= 2
end
result
end
puts fast_pow(2, 10) # => 1024
puts fast_pow(5, 13) # => 1220703125
三、带模的快速幂(常用于大数运算,比如 RSA)
def fast_pow_mod(base, exp, mod)
result = 1
b = base % mod
e = exp
while e > 0
result = (result * b) % mod if e.odd?
b = (b * b) % mod
e /= 2
end
result
end
puts fast_pow_mod(2, 1000, 1_000_000_007)
# => 688423210
扩展快速幂算法,让它支持 负指数。
Ruby 内置的 ** 运算符就是这样做的:
-
如果指数是非负整数,就用正常的快速幂。
-
如果指数是负数,就取倒数:
\[a^{-n} = \frac{1}{a^n} \]
实现:带负指数的快速幂
def fast_pow(base, exp)
return 1 if exp == 0
if exp < 0
# 负指数 => 倒数,结果要转为浮点数
return 1.0 / fast_pow(base, -exp)
end
result = 1
b = base
e = exp
while e > 0
result *= b if e.odd?
b *= b
e /= 2
end
result
end
puts fast_pow(2, 10) # => 1024
puts fast_pow(3, 5) # => 243
puts fast_pow(2, -3) # => 0.125
puts fast_pow(5, -2) # => 0.04
特点
- 当指数是负数时,返回
Float类型(和**一致)。 - 当指数是非负整数时,结果保持整数。
- 时间复杂度仍然是 \(O(\log n)\)。

浙公网安备 33010602011771号