算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !

今天和大家聊的问题叫做 Pow(x, n),我们先来看题面:

https://leetcode-cn.com/problems/powx-n/

Implement pow(xn), which calculates x raised to the power n (i.e. xn).

题意

实现 pow(xn) ,即计算 x 的 n 次幂函数。

说明:

-100.0 < x < 100.0

n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

样例

示例 1:输入: 2.00000, 10
输出: 1024.00000示例 2:输入: 2.10000, 3
输出: 9.26100示例 3:输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

解题

https://www.cnblogs.com/techflow/p/12740595.html

从题意来看,这道题平平无奇,基本上没有什么特别的。但是我们继续看它的note就会发现问题,其中x是浮点数,它的范围是-100到100。而n的范围则是32位int的范围,到这里就有问题了。

因为如果n很大,我们用循环去计算的话一定会超时。我们之前讨论过这个问题,即使是运算最快的C++,一秒种内的运算次数也只有10的8次方左右。而32位的int高达10的9次方,如果是Python的话这个运算会更慢,所以我们用循环肯定是无法得出结果的。

快速幂

这道题目把复杂度限制死了,我们从暴力方法入手是想不出结果的,必须要引入新的算法。而针对本题的算法就是快速幂

快速幂算法本质上也是二进制算法的变形,需要基于我们对二进制的充分理解,某种程度上来说它和多重背包问题当中的二进制拆分的解法比较近似。都需要对整数进行拆分,不过不同的是在背包问题当中拆分的结果进行的累加运算,而这里则是累乘。

如果你没有看过多重背包问题,也没有关系,我会从头开始将它讲清楚。

快速幂的算法好理解,但是看懂了很容易忘,尤其是初学的时候。学的时候理解了,过两天就忘了,或者代码写不出来了,这很正常。所以我们先从根本问题出发,从根源上理解它,而不只是运作原理。

第一个问题:我们使用快速幂的原因是什么?

这个问题很好回答,当然是因为啊,不然的话我们用循环计算幂不行么。但是为什么快速幂就快呢?为什么它比循环快呢?

这个问题哪怕我们没有学过快速幂的算法,也是可以回答的,它的答案也很简单,因为我们把一个原本不太好求的量转化成了一个很容易求解的量,从而降低了复杂度。说起来是废话的东西,但其实是很多算法的本质。算法也不是天上掉下来的,想出算法的人也不是凭空拍脑袋就出来的,最核心都是有一个原理可寻的。很多算法的核心原理,其实就是用转化和代替的方法求本来不是特别方便求的值。这个方法不仅在数学上常用,在算法上也是一样。

我们理解了这个核心之后,剩下的就简单了,我们知道不好求,因为我们现在没什么好的办法,那什么量是容易求的量呢?又该怎么转化呢?顺着这个思路,我们眼前的世界清楚了很多。

原本我们求解的方法就是通过循环,每次循环都乘上一个x,循环n次之后得到结果。我们观察一下这个过程,会发现我们在循环的时候,每一次循环,其实都代表了x的指数增加了1。也就是说它是线性增长的,当然就慢了。那什么增长比较快呢?指数增长比较快,比如我们一直翻倍翻倍,就很快。有一个关于指数增长的著名故事,说是从前有一个人发明了国际象棋。当地的国王非常喜欢下棋,于是他就把这个发明者召到了面前来说,你这个发明很好,我愿意奖赏你,你说吧,你想要什么,只要我能给的都行。

这个人说,陛下我想要的很简单,只要一些米。我希望第一个格子里放1颗米,第二个格子里放2颗,第3个格子里放4颗。每一个格子里放的都是前面的2倍,请陛下把这些米赏赐给国家里的穷人吧。

国王很震惊,觉得这个人是不是缺心眼,这么好的机会怎么只要了这么点赏赐。但是我们都知道,国际象棋一共有八八六十四格,我们按照这样放满,需要颗米。这显然是一个天文数字,别说一个国家了,就是全世界的米加起来也没这么多。故事里没提这个人最后的结局,很有可能因为戏弄国王被砍死了。但不管结局如何,至少说明了一个问题,指数增长和我们的直觉不符,它的变化极快

所以我们希望要是我们的指数也可以这样成倍地增长而不是每次只增加1就好了,那么我们怎么让指数成倍增长呢?这个就很简单了,平方就好了

我们把x平方就得到了,我们再把它平方就得到了,我们每次平方完,指数都翻了一倍,就好像刚才故事里的棋盘一样。所以我们只需要很少的次数就可以让指数变得很大。

我们解决了指数增长速度的问题,但是又遇到了新的问题,我们这样增长是很快,但是它翻倍再翻倍不一定就能得到n呀?

这个问题也简单,直接得到不可能就想办法呗。举个例子,比如我们的n=15,我们先找到小于15的最大的2的幂,发现是8。所以我们先得到了,我们把它放在一边之后还剩下了7,我们再来凑7,又找到了4,于是再放到一边,还剩下3,我们再来凑3……如此循环往复直到凑齐了n为止,n是一定可以这样凑到的,因为这本质上就是一个转化成二进制的过程。

我把整个过程画成了一张图,我们来看下图:

我们先算出所有2的幂,然后在算出所有x的2的幂次方。再把n拆成二进制,把二进制当中对应位置是1的值乘起来,就得到了结果。

有些同学可能不太熟悉二进制和位运算,我会提供两个版本的代码,帮助大家理解。我们先来看简单一些的代码:

class Solution:def myPow(self, x: float, n: int) -> float:base = []# 标记n是否为负数flag = n < 0# 把n置为正数n = abs(n)base.append(x)# 我们算出所有的x^2^ifor i in range(32):base.append(base[-1] * base[-1])ret = 1.0# 遍历32个二进制位# 如果i位为1,那么答案乘上base[i]for i in range(32):if ((1 << i) & n) > 0:ret *= base[i]# 如果n为负数返回1/retreturn 1/ret if flag else ret

在上面这段代码当中我们是先算出了每一个,然后我们再根据n转化成二进制的结果将它们乘在了一起。当然,我们熟练了之后是可以不这么费劲的,我们可以把这两步合并在一起,我们再来看一段代码:

class Solution:def myPow(self, x: float, n: int) -> float:base = []flag = n < 0n = abs(n)base = x    ret = 1.0# 我们在遍历二进制位的时候顺便求出x^2^ifor i in range(32):if ((1 << i) & n) > 0:ret *= base# x^2^i = x^2^(i-1) * x^2^(i-1)base *= basereturn 1/ret if flag else ret

总结

到这里关于快速幂的讲解就结束了,我个人感觉应该还是比较清楚的,算法的核心根基还是二进制,如果对二进制的概念掌握了,快速幂算法就是小意思了。

可能你会觉得奇怪,为什么非要用二进制而不是三进制、四进制呢,不是更快吗?原因有两个,一个是计算机底层基于二进制,我们暂时没有找到一个很好的材料可以实现三进制,因为二进制很简单,逻辑门开和关,带来高低电平表示状态就好了,但是三个状态用什么材料来实现呢?第二个原因是没有必要,多进制的确会更快,但是很有限,所以没有这个必要。

说来也不怕笑话,我在刚开始入门的时候,一直是用上的上面那种比较蠢的方法。而且放眼题解,至今没有找到一个人用这种蠢办法写快速幂的。但是代码蠢没有关系,能够运行,能够AC,能够理解才是关键。一开始写蠢点的代码没有关系,只要保持进步,以后自然会越来越好的。

好了,今天的文章就到这里,如果觉得有所收获,请顺手点个在看或者转发吧,你们的支持是我最大的动力。

上期推文:

LeetCode1-20题汇总,速度收藏!

LeetCode21-40题汇总,速度收藏!

LeetCode刷题实战40:组合总和 II

LeetCode刷题实战41:缺失的第一个正数

LeetCode刷题实战42:接雨水

LeetCode刷题实战43:字符串相乘

LeetCode刷题实战44:通配符匹配

LeetCode刷题实战45:跳跃游戏 II

LeetCode刷题实战46:全排列

LeetCode刷题实战47:全排列 II

LeetCode刷题实战48:旋转图像

​LeetCode刷题实战50:Pow(x, n)相关推荐

  1. ​LeetCode刷题实战603:连续空余座位

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  2. ​LeetCode刷题实战623:在二叉树中增加一行

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  3. ​LeetCode刷题实战371:两整数之和

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  4. ​LeetCode刷题实战375:猜数字大小 II

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  5. ​LeetCode刷题实战362:敲击计数器

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  6. ​LeetCode刷题实战450:删除二叉搜索树中的节点

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  7. ​LeetCode刷题实战546:移除盒子

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  8. ​LeetCode刷题实战276:栅栏涂色

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  9. ​LeetCode刷题实战488:祖玛游戏

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

最新文章

  1. [洛谷P1317]低洼地
  2. 小而美的个人博客——前端——types and archives
  3. TeraTerm设定(窗体大小,字体字号)保存为默认值
  4. 漫谈高数——泰勒级数的物理意义
  5. 作者:熊刚,男,博士,现任中国科学院自动化研究所研究员,中国科学院云计算中心自动化所东莞研究院首席科学家等职务。...
  6. Android面试,View绘制流程以及invalidate()等相关方法分析
  7. 看CarbonData如何用四招助力Apache Spark
  8. change project compliance and jre to 1.5
  9. 【nginx】nginx 高可用集群
  10. 编译fdk-aac for ios
  11. [PyTorch] 损失函数
  12. ascii码与键盘代码的区别
  13. 软件需求说明书模板概要书
  14. esp32FreeRTOS教程——内核分配
  15. 让html img图片垂直居中的三种方法
  16. 如何防止亚马逊账号关联的一些建议值得卖家们收藏?
  17. python signal滤波器使用说明
  18. Selenium IDE安装与运行
  19. 安科瑞无线测温装置ARTM的功能特点有哪些
  20. 【数位板常见问题】压感笔为什么没有压感了

热门文章

  1. 律师点评——员工因“对公司的赞美不合格”被淘汰
  2. 用HK-MSR165微型振动冲击记录仪记录动态机械应力,帮助优化机器、工件和生产
  3. 基于Android的小说阅读app的设计与实现
  4. 黑客集团揭密--“死亡军团”
  5. linux生成initrd,手动创建系统启动镜像文件:initrd.img
  6. Android 性能优化最佳实践
  7. C语言集106-111
  8. thinkphp支付宝资金下发 单笔转账
  9. 滑动平均模型原理+源码分析
  10. 水质检测系统(Python图像识别)