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)\)
posted @ 2025-09-03 16:47  立体风  阅读(12)  评论(0)    收藏  举报