快速幂问题详解

  本文讲讲快速幂问题,之前在一篇超级次方的博客简单提到过,链接如下。但是没有深入讲讲,只是简单介绍了一下快速幂的思想。

  快速幂的思想是分治,在超级次方一文里面的分治是按不同的位数来进行分治,比如我们要算5234的值,如果我们直接算234个5相乘,计算次数是很多的,是235次,但是我们可以采用分治的思想,比如234可以拆解成2100+310+4,那么5234=52*100+3*10+4=52*100*53*10*54,这样幂的次数就降低了很多,关于原来的式子还可以进一步分治,将每次的幂分解为10以内的式子,如52*100= ((52)10)10,以此类推,从而将每个幂将为10以内的数字,来减少运算次数,分治之后我们的计算次数为3+2+1=6次,可见运算次数大大减少。但是该方法需要额外的维护幂,在大多数情况下其实不常用,常用的是二分递归,如下所示,直到最后的幂为0为止。

  • 5234=5234/2*5234/2=5117*5117
    5117=5117-1*5 (117为基数,因此要拆解为an=an-1*a)
    5116=5116/2* 5116/2

  这个思想还是很简单的,主要是代码的两种实现,先从最经典的递归讲起,代码如下:代码逻辑是这样的:当前幂为偶数,直接递归公式为func(d, p//2)**2 , 若为奇数,则递归公式为func(d, p-1)*d,如果大家看懂了上面的二分计算过程,到这里应该没啥问题。由于我们是求解一个连续乘法,所以每一步判断语句里面都要有返回值,这也是求解此类问题(连加、连乘、连减、连除)的一个思路,首先列出所有的递归条件,然后理清每个递归条件要对应的返回值,这样写递归就不会如无头苍蝇了。

def func(d, p):
    """
    d为底,p为幂,返回pow(d, p)的值
    >>>func(2, 10)
    >>>1024
    """
    if p == 0:
        return 1
    elif p % 2 == 1:
        return func(d, p-1)*d
    else:
        return func(d, p//2)**2     
func(2, 10)

  递归的代码虽然清爽,但是大家都知道,递归的实现原理是基于内存里面的栈,如果递归深度过大,在本例中如果求解的是2123141242144次方,那么内存需要维护极多的栈,会产生很多的额外开销,因此能递归解决的算法题建议大家还是都用迭代实现。下面是介绍两种快速幂的迭代实现。

  • 第一种,大众流快速幂实现:
def func(d, p):
    """
    d为底,p为幂,返回pow(d, p)的值
    >>>func(2, 10)
    >>>1024
    """
    ans = 1
    while p:
        if p % 2 == 1:
            ans = ans*d
        d *= d
        p = p//2
    return ans
func(2, 8)

  代码解读。我们要知道在迭代里面的幂的实际价值,是计数,即计算pow(d, p)二分的次数,举个简单的栗子,比如要计算28,那么计算次数就是8//2=4,4//2=2,2//2=1,共计三次二分,对应的计算结果是2*2=4,4*4=16,16*16=256,最后p=1的时候,把这个结果乘到ans上,即为最终计算结果,这是偶数的栗子。再举一个奇数,比如29,那么计算次数9%2==1,ans=2,9//2=4,4//2=2,2//2=1,共计三次二分,对应的计算结果是2*2=4,4*4=16,16*16=256,最后ans = 2*256,即为最终计算结果。所有的数都是这样,那么我们的奇数情况,p-1在哪体现呢?体现在p = p//2是向下取整,所以自然而然的就有了减1的操作,同时当 p % 2 == 1的时候,我们把这个减去的1次幂,乘给了ans。

  • 下面介绍一种比较清爽和高端的迭代写法,代码如下。
def func(d, p):
    """
    d为底,p为幂,返回pow(d, p)的值
    >>>func(2, 10)
    >>>1024
    """
    ans = 1
    while p:
        if p&1:
            ans = ans*d
        d *= d
        p >>= 1
    return ans
func(2, 8)

  上面的代码用了二进制的知识,>>是右移,>>1表示把二进制数往右移一位,相当于对2取整的意思;&是按位与,&1可以理解为取出二进制数的最后一位,相当于%2==1。&好理解,这里简单说一下为什么>>1就是对2取整的意思,这里可以用10进制类比,比如一个十进制数1234,我们往右移了一位变成了123,是不是相当于是1234对10取整,2进制也是一样,往右边移一位,相当于前面的位对应2的次幂都要-1,因此等价于对2取整。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lemon_tttea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值