我目前正在阅读Skiena的“算法设计手册”。
他描述了一种计算数字幂的算法,即计算a^n
。
他首先说最简单的算法只是a*a*a ... *a
,所以我们总计n-1
次计算。
然后他继续说有一个优化,并要求我们认识到:
n = n/2 + n/2
我们也可以说
a^n = ((a^n/2)^2) (a to the n equals a to the n over 2, squared)
到目前为止,我了解到这一点。根据这些方程式,他推导出一种仅执行O(lg n)
次乘法的算法。
function power(a, n)
if (n = 0)
return(1)
x = power(a,n/2)
if (n is even)
return(x^2)
else
return(a*x^2)
似乎x
必须是到目前为止计算的当前值。但是在读完这几次之后,我仍然不明白他是如何从这些方程中设计出这种算法,甚至是如何工作的。谁能解释一下?
答案 0 :(得分:15)
这个概念很简单。例如,计算3 8
的值你可以使用明显的方法,即3 8 = 3 x 3 x 3 x 3 x 3 x 3 x 3 x 3,这需要7次乘法运算。或者有更好的方法。
我们说
向后移动,只需要3次乘法来计算3 8 而不是7
以下是该过程的清晰视图:
32 = 3 x 3 = 9 34 = 32 x 32 = 9 x 9 = 81 38 = 34 x 34 = 81 x 81 = 6,561
然后,还有另一个问题,如果功率是奇数,该怎么办?例如:3 9 ,如何处理它?你可以这样做
39 = 3 x 38 or 39 = 3 x 34 x 34
让我们将连续多次数的算法调用为方法A ,并将连续将功率除以2的算法称为方法B 。方法A与方法B相比有多好?对于少数如3 8 ,没有太大的改进,即使是那些,我们最小化乘法的数量,但我们也略微增加除法运算的数量。
因此对于3 8
Multiplication Division Method A: 7 0 Method B: 3 3
然而,对于更大的功率,例如:3 4,294,967,296
Multiplication Division Method A: 4,294,967,295 0 Method B: 32 32
差异很大。
答案 1 :(得分:7)
首先,让我们修复你的算法:
function power( a, n )
if (n = 0)
return(1)
x = power(a,n/2)
if (n is even)
return(x*x)
else
return(a*x*x)
假设您要计算power(2,8)
,即2^8
(^
当然不是XOR
。
1)(a=2
,n=8
)。 2^8 = (2^4)^2
,因此我们必须计算x=2^4
,然后计算x*x
以得出最终结果。我们必须再次致电power()
以获取x
。 power(2,4)
。
2)(a=2
,n=4
)。 2^4 = (2^2)^2
,因此我们必须计算x=2^2
,然后计算x*x
以得出最终结果。我们必须再次致电power()
以获取x
。 power(2,2)
。
3)(a=2
,n=2
)。 2^2 = (2^1)^2
,因此我们必须计算x=2^1
,然后计算x*x
以得出最终结果。我们必须再次致电power()
以获取x
。 power(2,1)
。
4)(a=2
,n=1
)。 2^1 = (2^0)^2
,因此我们必须计算x=2^0
,然后计算a*x*x
以得出最终结果。我们必须再次致电power()
以获取x
。 power(2,0)
。
5)(a=2
,n=0
)。 2^0 = 1
因为n
是0
,所以我们将x
的值返回到第4步。
4)(a=2
,n=1
,x=1
)。此步骤的最终结果为a*x*x = 2*1*1=2
,这是在步骤#3中分配给x
的值。
3)(a=2
,n=2
,x=2
)。此步骤的最终结果是x*x = 2*2 = 4
。这是在步骤#2中分配给x
的结果。
2)(a=2
,n=4
,x=4
)。此步骤的最终结果是x*x = 4*4 = 16
。这是在步骤#1中分配给x
的结果。
1)(a=2
,n=8
,x=16
)。此步骤的最终结果是x*x = 16*16 = 256
。这是power(2,8)
返回的结果。
答案 2 :(得分:0)
x = power(a, n/2)
会给你一个^ n / 2。如果整个陈述都是平方给出(a ^ n / 2)^ 2。现在,如果n是奇数,则在n / 2期间,^ 1的幂会丢失,因此要将其恢复,则将其乘以a。这是根据给定的等式。
答案 3 :(得分:0)
这个函数是递归的,这意味着当被调用时,它会一遍又一遍地调用自己,直到满足某些最终条件。在这种情况下,有三个最终条件将阻止函数调用自身并返回结果。
如果我是你,我会尝试在几个不同的值上手动应用算法,以便理解。
答案 4 :(得分:0)
这个公式a^n = ((a^n/2)^2)
,你理解它决定了递归算法。
要获得a^n
,您需要先计算a^(n/2)
,
要获得a^(n/2)
,您需要计算a^((n/2)/2)
,
......依此类推,直到(n/2/2/...2)
达到0,这是递归的终止条件。
因此,递归算法完全遵循该公式:
获取power(a,n)
您首先递归计算power(a,n/2)
,然后返回调整n
为奇数/偶数的结果。
还有关于此实施的wikipedia文章。
答案 5 :(得分:0)
函数power(...)
似乎是按照处理整数除法效果的方式编写的。回想一下,在整数除法规则下,小数部分被丢弃。这只会影响奇数整数,因为偶数整数除以2不会产生余数。
因此,只要n
是偶数整数,为n/2
计算的值就等于n/2
,并且可以采用if
的顶部分支:这是正好是方程式所规定的内容。
每当n
为奇数时,n/2
的计算值实际上等于floor(n/2)
。换句话说,声明:
x = power(a,n/2)
实际上已计算出结果
x = power(a,(n-1)/2)
意味着你已经从你的指数中永久丢失了一个,而只是返回x^2
将是a
的一个强大的力量。因此,底部分支增加了a
的力量。
如果您想象而不是整数除法,计算机能够完全无损实数除法,那么您可以将函数重写为:
function power( a, n )
if (n = 0)
return(1)
x = power(a,n/2)
return x^2
您可以轻松地确信这是您在问题中提供的第二个等式的简单递归实现。