使用递归和 mod 计算指数的算法

Posted

技术标签:

【中文标题】使用递归和 mod 计算指数的算法【英文标题】:Algorithm to calculate exponent using recursion and mod 【发布时间】:2019-05-17 23:02:05 【问题描述】:

我被教导了一种使用 mod 和递归计算指数的不同方法,但我并不完全理解它。方法是:做b^e,我们可以这样分解:

  q = e div 2
  r = e mod 2
then e = 2q+r, and r could be 0 or 1.

If r=0:

    b^e = (b^q)^2

If r=1:

    b^e = (b^q)^2 * b

base case: b^0 = 1.

例如:2^2, b=2, e=2

q = 2/2 = 1
r = 2mod2 = 0

r=0, therefore 2^2 = 2^1^2

我正在尝试对此进行编码。

pow :: Integer -> Integer -> Integer
pow b e
    | e == 0 = 1
    | r == 0 = pow (pow b q) 2
    | r == 1 = b * pow (pow b q) 2
  where
    (q, r) = divMod e 2

但代码不会在e!=0 的任何时候结束,例如pow (-2) 4pow 1 1 永远持续下去。知道为什么吗?

【问题讨论】:

不,不知道。或许您应该包含确切的错误 - 就此而言,包含导致它的完整代码,因为此代码本身不会。 @DanielWagner 好的,我编辑了帖子以提供更精确的错误。 这与基本库用于实现(^) 的算法基本相同,因此您可能需要查看its source code。 【参考方案1】:

如果您尝试手动评估pow b 2,您很快就会明白原因。从divMod 2 2 = (1, 0) 开始,我们从pow b 2 扩展到pow (pow b 1) 2。请注意,这也是形式为pow b' 2,与b' = pow b 1。所以我们得到了一个无限链:

pow b 2
=
pow (pow b 1) 2
=
pow (pow (pow b 1) 1) 2
=
pow (pow (pow (pow b 1) 1) 1) 2
=
...

有几种方法可以解决它。您可以为e == 2 添加一个基本情况,或者不用递归调用两次pow,您可以自己进行乘法运算(如在现有代码中将pow foo 2 替换为foo * foo)。

【讨论】:

【参考方案2】:

您还需要提供e 何时为2 的基本情况:

pow b 2 = b * b

没有这个,你的递归不会结束,因为它变成了pow (pow b 1) 2,你什么也得不到。

【讨论】:

这也可以通过写let a = (pow b q) in a*a而不是pow (pow b q) 2来解决,这会重复太多。【参考方案3】:

正如前面的答案中提到的,您的代码几乎可以工作,只是允许递归停止。

请参阅下面的代码以获取可能的修复。递归调用的参数最多是当前参数的一半,因此递归将不得不停止。

另一方面,这个算法已有 2000 多年的历史,起源于古印度。请以应有的尊重对待它:-) https://mathoverflow.net/questions/107708/origin-of-square-and-multiply-algorithm

pow :: Integer -> Integer -> Integer
pow b e
    | e == 0 = 1
    | r == 0 = let bpq = pow b q  in  bpq*bpq
    | r == 1 = let bpq = pow b q  in  bpq*bpq*b
  where
    (q, r) = divMod e 2

main = do
    let b = 3 :: Integer
    let e = 7 :: Integer
    let x = b^e
    putStrLn ("b^e     = " ++ show x)
    let y = pow b e
    putStrLn ("pow b e = " ++ show y)

【讨论】:

以上是关于使用递归和 mod 计算指数的算法的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript:试图理解计算指数值的递归函数的 Else 语句

斐波那契递归的优化及指数计算的优化

《算法竞赛进阶指南》-AcWing-92. 递归实现指数型-题解

《算法竞赛进阶指南》-AcWing-92. 递归实现指数型-题解

递归思想(钻出牛角尖)

这个用于计算指数的递归代码的运行时间是多少?