python详解(8)——进阶(2):初步算法

目录

🏆一、前言

🏆二、时间复杂度

🏆三、递推

🚩1.简介

🚩2.爬楼梯

🚩3、猴子吃桃

🏆四、递归

🚩1、简介

🚩2、递归求斐波那契数列

🚩3、递归求阶乘

🏆五、穷举法

🚩1、简介

🚩2、百钱买百鸡

​编辑

 🚩3、组合数字

🏆六、贪心算法

🚩1、简介

🚩2、背包与宝物(中等)

🚩3、跳跃游戏(困难)

🏆七、分治法

🚩1、简介

🚩2、a^b

🏆八、动态规划法

🚩1、简介

🏆九、区分

🏆十、尾声


🏆一、前言

好消息:放假了yeeeeeeeeeeeeee~

更好消息:喜羊羊与灰太狼新剧开播了yeeeeeeeee~

坏消息:要写文

在上一篇文章当中,我们了解了8个排序算法。这只是算法的冰山一角。我们接着来了解和学习。

这次,我们了解的是算法的深入认识及各种方法,还有各种例题。

写作不易,给个三连支持一波!


🏆二、时间复杂度

上一篇文章已经学过了算法的基本定义,这里不再重复阐述。

首先,我们来了解算法的时间复杂度。

时间复杂度,是来衡量一个算法用时的东西。用O符号来记录。

注意,这里面的时间不是真正运行的时间,只是相对于输入的。

时间复杂度常见的有5种:

常数时间O(1)。意思就是无论输入多么复杂,或者多么简单,他所运行的时间都是固定的数值。例如有一个算法是要求一个列表的第一位,代码如下:

def abc(li):return li[0]

这里面,无论输入多么复杂,最终程序的用时都是固定的。

线性时间O(n)。n的意思就是输入规模大小。他的意思就是,所用的时间是和输入规模大小成正比的。这多用于for循环遍历。例如,要把一个列表的每位元素分开输出,代码就是:

def abc(li):for i in li:print(i)

这里面,列表li越长,运行时间就按正比例关系增加。

对数时间O(log n)。对数时间一般是二分查找的时候。例如大家小时候玩过一个猜数字的游戏:从1-100里的任意一个随机数中,找到一个数字,每次猜测会提示你猜大了还是猜小了。

这个问题的最优解法是:一直在固定的范围内折半猜。例如1-100就猜50,50到100就猜75,这种方法是最快的。

如果要写一个程序来猜这个数,它所用的时间就是log n。这里面的底数是2。

线性对数时间复杂度O(n log n)。是不是有点难理解?

我们之前写的归并排序、快速排序的时间复杂度就是O(n log n)。回味一下快速排序代码:

 这里面,我们用一个while循环O(n)一直进行三分O(log n),最后返回这段程序一直循环进行O(log n),组合在一起就是O(n log n)。

平方时间O(n^2)。平方时间运行是有点慢的。典型的示例就是嵌套循环。例如,把一个数组的每两个元素进行比较,看能组合多少个。这么简单的代码直接return len(li)**2就行了,可有一个大聪明非得整一个嵌套循环,代码就是:

def abc(li):count=0for i in li:for j in li:count+=1return count

这里面,它的时间复杂度就是就是O(n^2)。这属于一种低效的时间复杂度。

指数时间O(2^n)。他运行的时间呈指数型增长。

程序实例:一个递归版本的斐波那契数列。

def fibonacci(n):if n <= 1:return nelse:return fibonacci(n-1) + fibonacci(n-2)

这里面,运行时间就是指数增长的。


除了这五种常用的时间复杂度,还有几种。

O(n^3),立方时间,一般是三重嵌套循环。

O(m*n),m*n时间,m和n分别代表两个输入的大小。一个量不变,运行时间就会随着另一个量的增长而增长。如果两个量都变,运行时间则会更慢。

O(log log n),双对数时间,一般是一些基于分治法、反证法的算法。

O(sqrt(n)),根号时间,一般是求解质数、因子的问题。

O(n!),阶乘时间,一般是用来组合的暴力解法。例如我们都熟悉的问题:n把锁n个钥匙,至少要试几次才能打开所有锁。一直尝试来解的话,他的时间复杂度就是O(n!)。

除此之外还有好多种。这里不再解释。

另外,时间复杂度是有最优和最差的。例如一个不稳定的排序算法,他的运行时间也不固定。


在上一篇文章中:

冒泡排序:O(n^2)

快速排序:最优O(n log n),最差O(n^2)

插入排序:最优O(n),最差O(n^2)

选择排序:O(n^2)

计数排序:O(n+k)。这是一种特殊的时间,n是列表长度,k是列表里最大的数。

归并排序:O(n log n)

基数排序:O(d(n+k))。还是一种特殊的时间。其中n是列表长度,k是最大元素的范围,d是最大元素的位数。

bogo排序:啥也不是


🏆三、递推

🚩1.简介

接下来,我们学习算法中的递推法。

递推,就是一步一步由已知推向未知。最后得出需要的结果。

例如,推算出斐波那契数列中,某一个值后面的那一个数,只给你1,1为起始条件,这就要用到递推的方法。先推算第三位,再第四位,再第五位······直到推算出结果。代码如下:

def Fibonacci(num):a = 1b = 1while True:a = a+bif a >= num:return ab = a+bif b >= num:return b
num = int(input())
print(Fibonacci(num))

输入:114

输出:144

就像这样,逐步推出结果。

有些递推法解决的问题,表面很复杂,但一般有这两种方法提供思路:

1.列举法,列举一些结果,看他们有什么规律。

2.关系式法,列出来每个量之间的关系式,可以是数列。

递推分为顺推和倒推,倒推就是根据一个问题的结果来输出他的条件。

话不多说,我们直接来几道例题。

🚩2.爬楼梯

来一道经典的爬楼梯问题。

一个楼梯有n阶台阶,你每次可以选择一次跨2格或者一次跨1格,问你爬上去有多少个解法?

这题,你们可能只是不会写代码,我连数学解法都不会······我们来找一下规律:

1:1个2:2个3:3个4:5个5:8个6:13个······

1,2,3,5,8,13,熟悉吗?

这是斐波那契数列。按照斐波那契数列的方法来推就行了。

def palouti(num):if num == 1 or not num:return numa = 1b = 1c = 1while True:a = a+bc += 1if c == num:return ab = b+ac += 1if c == num:return b
while True:num=int(input())print(palouti(num))

最终代码。

🚩3、猴子吃桃

猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想吃时,只剩下一个桃子了。求第一天共摘多少个桃子。

我们先不管这个猴子是不是有什么大饼,也不管他的饮食规不规律,先列关系式再说。

我们用关系式来解决。

第n天的桃子数量=第n-1天的桃子数量/2-1

则,第n-1天的桃子数量=(第n天的桃子数量+1)*2

第十天有1个桃子,我们只要把他+1,再*2就行,循环10次。

代码:

def abc():num = 10a = 1while True:a = (a+1)*2num -= 1if num == 1:return a
print(abc())

🏆四、递归

🚩1、简介

递归上一篇文章已经讲过了,我们再回味一下。

递归,就是通过一个函数在程序内调用自身来减少代码量。递归的方法需要一个边界条件,不然会无限循环;当到达递归边界时,程序会一层层向上返回,最后得出想要的结果。

可以理解成一个傻*查字典,他查了一个词,发现这个词语解释里面有不会的词,于是又去查第二个词,但是第二个词的解释里面还有不会的词,于是就去查第三个······直到有一个词语的注释他可以看懂(递归边界条件),这时候,他就可以理解倒数第一个词,倒数第二个词······最后理解最开始的词。

递归有3个特点:

1、必须有一个明确的结束条件,就是递归边界。

2、每次进入更深一层的递归,计算量相比于上一层都会有所减少。

3、递归次数过多会导致栈溢出。

递归注重的是简洁与优雅,至于速度那玩意儿谁爱考虑谁考虑。

递归中,你创建的每一轮递归都叫做栈。栈的数量是有限制的。一般是在1000左右。超过就会报错。这叫做栈溢出。

话不多说,直接上题:

🚩2、递归求斐波那契数列

 求斐波那契数列的第n个数。

这题大家可能感觉无从下手,因为递归写起来比较难,但是我们只要理清一个关系:

S(n)=S(n-1)+S(n-2)

S为斐波那契数列,n表示第n项。

然后我们可以一直求下去。

S(n-1)=S(n-2)+S(n-3)

S(n-2)=S(n-3)+S(n-4)

······

最后一直到斐波那契数列的第1、2位,这就是递归边界条件。直接返回1。因为斐波那契数列的第1和2项都是1。

代码:

def Fib(n):if n == 1 or n == 2:return 1return Fib(n-1) + Fib(n-2)
n=int(input())
print(abc())

行量这么少也是十分的神奇,但是时间复杂度达到了恐怖的O(2^n)。

🚩3、递归求阶乘

接下来,我们用递归求n的阶乘。

我们找一下规律。

1!=12!=2x1!=23!=3x2!=64!=4x3!=245!=5x4!=120

就是这样。思路已经十分明确了,相信大家自己也能写出来代码。注意:当n=0或者1时为递归边界条件,直接返回1。代码:

def f(x):if x == 0:return 1elif x == 1:return 1else:return(x * f(x-1))
while True:print(f(int(input())))

🏆五、穷举法

🚩1、简介

穷举,顾名思义就是把问题结果所有可能出现的解试一遍,直到试出答案。

这种方法出现率很少,一是因为大多数算法用不着穷举,二是因为时间复杂度高。

大家应该能想到,穷举方法的时间肯定是很慢的。提升时间的方法就是:不用穷举尽量缩小穷举范围。前提是你能确保答案在这个范围内。

不多说了,先来几道例题:

🚩2、百钱买百鸡

一个人有100块钱,雄鸡5块1只,母鸡3块1只,小鸡1块3只,他100块钱刚好买了100只,问雄鸡母鸡小鸡各买了几只(列出所有可能,不会有一类鸡买了0只)。

假设雄鸡x只,母鸡y只,小鸡z只:

x+y+z=1005x+3y+z/3=100(x,y,z∈N+)

穷举法解决这个问题,首先要明确每类鸡最多能买几只。

雄鸡最多20只,因为21只就超过了100块钱。母鸡最多33只。小鸡最多300只,但是他买了100只鸡,所以最多100只。

接下来,就是穷举遍历了。如果满足上述2式则print。

我们还有一个加速点:小鸡。因为小鸡1块3只,所以小鸡的数量一定是3的倍数,所以我们遍历小鸡的时候,步长可以设置为3。

最终代码:

for x in range(1, 21):for y in range(1, 34):for z in range(3, 100, 3):if (x*5 + y*3 + z/3 == 100 and x+y+z == 100):print("公鸡:",x,"母鸡:",y,"小鸡:",z)

有人有疑问了:遍历雄鸡是1到20,母鸡是1到33,都很正常,遍历小鸡的时候为什么遍历的是3到99呢?

步长是3,不代表是要遍历3的倍数。如果是正常遍历1到100,则遍历的是1,4,7,10······如果遍历的是3-100,则遍历的是3,6,9······这才是正确的,而且不会出现多出或遗漏。最终结果:

 🚩3、组合数字

有三个数为a,b,c,他们的和为19,取值范围为1-9,问有多少种可能?(a!=b!=c)

很简单,先创建一个计数变量。每一个数都遍历1到9,如果a+b+c=19,且三者都不相等则将计数变量自增。最后输出计数变量。最终结果为30。

count = 0
for a in range(1, 10):for b in range(1, 10):for c in range(1, 10):if a+b+c == 19 and a != b and a != c and b != c:count += 1
print(count)

🏆六、贪心算法

🚩1、简介

这种算法使用率很高,通常是一步一步进行,总是会做出从当前来看最优的选择。他不能求出所有问题的最优解,但能求出大部分问题的近似最优解。

因为大多数问题不会让你求近似最优解,你如果要在这类问题上使用贪心算法,就必须先证明:结果一定最优。

不说了,看例题!

🚩2、背包与宝物(中等)

有一个人背着背包,他最多能背动150单位重的东西。现在有以下宝物:

ABCDEFG
重量35306050401025
价格10403050364030

问:背哪些宝物可以价值和更高?

这个问题,用贪心算法就只需要求出重量与价格比值,之后从大到小排列。

我们来注意一下,如果遍历到最后怎么办?比如遍历到最后还有35单位,宝物还有BFG没装,那么该选择哪个?

贪心算法会先选择F,之后选择B。但是程序会发现超重了,这时候,贪心算法的弱点就暴露出来了:有的问题求不出来最优解。程序会放弃选择,直接留下25单位的空间浪费。

如果在超重时进行侦测并且找到不超重且最贵的宝物进行获取,那我们可以想象,如果宝物是这样的,背包容积114518:

A宝物重114514,钱1919810

B宝物重3,钱8

C宝物重0.5,钱1

如果让上述的算法进行筛选,他就会选择A,再选择D。然后就没有然后了。

只能再加强一下算法,进行多个物品的选择,那这样就又回到了上述程序。

显然:如果求出来最优解,需要递归。

我们给原问题增加一个条件:宝物可以分割,并且重量与价值比不变,不然寿命要没了。这样遇到超重直接给他分割就行。

代码懒得写辣!

🚩3、跳跃游戏(困难)

给定一个非负整数数组,最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断是否能够跳完列表。

用贪心算法解决,我们要解决的就是:当自己在一个位置上时,尽力跳得远。

侦测流程:

1、设自己所在位置为n,n的后n位的列表为li,其中第一个数字为k。如果n-k<n到k的距离,则直接跳过去,是一定赚的。

2、反之,如果n-k>=n到k的距离,跳过去就不会赚,不能跳。

3、如果2成立,我们则让k侦测第二个数,还是不能再侦测第3个数······只要有成立就跳到那个数字上,不成立则继续下一个数字的判断。如果li里面的数字都亏了,则不能逃出这里,输出False。

4、如果更新n、li的时候出现异常则是遍历到了末尾导致越界,直接输出True。

步骤很复杂?简化一下:

设置n、li
死循环:用k遍历range(li):比较 n-li(k)和k+1,如果<:错误尝试:更新n、li并break退出循环如果报错,就是说已经遍历到了末尾导致越界返回True如果循环是正常退出:返回False

具体代码就不写了。作者没有去写出来并运行代码,如果有不河里的地方请指出!

🏆七、分治法

🚩1、简介

分治法,就是把一个问题分成若干个小问题最后连起来。和贪心算法是不是有点相似?最大的区别就是:贪心算法是走到哪一步考虑哪一步,分治法是直接把大问题分成若干个小问题再分别解决。

还是很好理解,直接看题:

🚩2、a^b

求a^b。这里不是简单地a^b,是王维诗里的a^b啊不,是用分治法求a^b。

你们可能感觉:这么简单的一个幂运算怎么能分呢?

我们来分治一下:

如果a是负数{如果a是奇数如果a是偶数}如果a是正数{如果a是奇数如果a是偶数}

我们要做的,就是把负奇、负偶、正奇、正偶这四种情况考虑。是不是有点像数学的分类讨论?

如果a是偶数的话,也就是a%2==0:

a^{b} = a^{\frac{b}{2}+\frac{b}{2}}  = a^{\frac{b}{2}}*a^{\frac{b}{2}}

如果a是奇数的话,也就是a%2==1:

a^{b} = a^{\frac{b-1}{2}+\frac{b-1}{2}+1} = a^{\frac{b-1}{2}}*a^{\frac{b-1}{2}}*a

如果b是负数,还是上面那一套方法,再用1去除就行。

完成这个代码呢,要浅浅得用一下递归。准确来说不算递归,就是调用一两次自己而已。代码:

def _pow(a, b):if a == 0 and b == 0:return Noneelif b == 0:return 1elif a == 0:return 0elif b < 0:return 1/_pow(a, -b)elif b%2 == 0:return _pow(a*a, b//2)#利用积的乘方:a^n*b^n=(ab)^n,这里a=belse:#仅剩的选择:b为正奇数return _pow(a*a, b//2)*a
while True:a=int(input())b=int(input())print(_pow(a, b))

🏆八、动态规划法

🚩1、简介

动态规划,就是把一个大的复杂问题分成若干小问题,求出这些小问题的最优解再合起来。这些小问题都具有相同特征。

动态规划的主旨就是动态和规划。

是不是又像贪心又像分治?

他仨长得确实有点像,后面会进行详细讲解。不多说了,来几道例题:

······

我去,救命,一道简单的例题都搜不出来。跳过跳过。

🏆九、区分

这么多算法方法之间,大家可能察觉出了很多联系。接下来,我们将比较相似的方法进行区分:

递推与递归的区别:

        递推是数学上的概念,指的是递推式、数列等等这些,将思想迁移进行应用。

        递归则是一个函数调用自身,来减小代码量。

分治法和动态规划法的区别:

        二者都要将问题分成几个小问题再合并求解。

        不同的是在分治法中,小问题之间没有太大联系。

        而在动态规划法中,则是多个同样的小问题的重叠,避免对同一个问题的重复计算。

分治法和贪心算法的区别:

        分治法是将问题分成多个独立小问题再分别求解。

        贪心算法则是将问题分成了多个阶段,每个阶段做出局部最优选择。

        分治法需要合并最后的独立小问题,贪心算法则是走哪儿算哪儿,不需要合并。

🏆十、尾声

我真的不是故意花一个月屑文的······

写作不易,求个三连支持~

-----------------------------------------------------end----------------------------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/22537.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“开放合作 共享未来”华秋联手伙伴共创硬件生态,助力物联网硬件加速创新

2023年7月11日&#xff0c;华秋携产品与方案亮相慕尼黑上海电子展&#xff08;electronica China&#xff09;&#xff0c;并与5家生态伙伴签署硬件生态共创战略协议&#xff0c;通过“硬件软件供应链”的合作模式&#xff0c;发挥各自行业优势&#xff0c;共同推动电子产业的创…

springboot时间管理系统

通过前面的功能分析可以将时间管理系统的功能分为管理员&#xff0c;用户两个部门&#xff0c;系统的主要功能包括首页&#xff0c;个人中心&#xff0c;系统公告管理&#xff0c;用户管理&#xff0c;时间分类管理&#xff0c;事件数据管理&#xff0c;目标数据管理&#xff0…

k8s 持久化存储

我们继续来查看 k8s 的卷&#xff0c;上一次我们分享了将磁盘挂载到容器中&#xff0c;empyDir 和 gitRepo 都是会随着 pod 的启动而创建&#xff0c;随着 pod 的删除而销毁 那么我们或许会有这样的需求&#xff0c;期望在 pod 上面读取节点的文件或者使用节点的文件系统来访问…

uniapp下上传图片后图片裁剪加图片旋转,支持H5和app

效果图 代码如下 <template><view class"container" v-show"isShow"><view><view class"cropper-content"><view v-if"isShowImg" class"uni-corpper":style"width: cropperInitW px;he…

Docker 安装 Nacos 单节点

Docker 安装 Nacos 单节点 1 搜索 Nacos2 下载 Nacos3 安装 Nacos Nacos&#xff08;中文名“云注册中心和配置中心”&#xff09;是一个用于动态服务发现、配置管理和服务管理的开源项目&#xff0c;它由阿里巴巴集团开发并开源。Nacos提供了一种简单而强大的方式来实现微服务…

【力扣JavaScript】1047. 删除字符串中的所有相邻重复项

/*** param {string} s* return {string}*/ var removeDuplicates function(s) {let stack[];for(i of s){let prevstack.pop();if(prev!i){stack.push(prev);stack.push(i);}}return stack.join(); };

no-unused-vars

找到 package.json 在rules输入 "no-unused-vars":"off"

PADS Logic怎么显示与隐藏元件的管脚编号和管脚名称

在绘制原理图元件的时候&#xff0c;有时管脚数量过多&#xff0c;管脚编号会显的特别密。既可以选择隐藏管脚编号&#xff0c;显示主要目的就是分辨出信号管脚。 第一步&#xff1a;在创建元件界面&#xff0c;执行菜单命令设置-显示颜色&#xff0c;如图1所示 图1 显示颜色选…

InsCode Stable Diffusion使用教程【InsCode Stable Diffusion美图活动一期】

记录一下如何使用 InsCode Stable Diffusion 进行 AI 绘图以及使用感受。 一、背景介绍 目前市面上比较权威&#xff0c;并能用于工作中的 AI 绘画软件其实就两款。一个叫 Midjourney&#xff08;简称 MJ&#xff09;&#xff0c;另一个叫 Stable Diffusion&#xff08;简称 …

EMQ 联合英特尔、云轴科技 ZStack 推出泛工业物联网联合解决方案

近日,EMQ 携手英特尔与云轴科技 ZStack 推出泛工业物联网联合解决方案,基于云原生超融合,在挖掘生产数据价值的同时有效降低综合建设成本,为用户提供一站式数据链路及 IT 基础设施解决方案。 工业能耗大户面临的关键挑战 工业正迈入一个全新的物联网时代,海量数据计算需求涌现…

微信加粉计数器后台开发

后台包括管理后台与代理后台两部分 管理后台 管理后台自带网络验证卡密系统,一个后台可以完成对Pc端的全部对接,可以自定义修改分组名称 分享等等代理后台 分享页 调用示例 <?php$request new HttpRequest(); $request->setUrl(http://xxxxxxx/api); $request->…

Redis特性初识及其安装与配置

目录 1.认识Redis Redis主要特点 主要应用场景 2.MySQL VS NoSQL 3.Redis的安装与配置 redis5的安装 修改配置文件 启动redis 4.Redis客户端 命令行客户端 图形化界面客户端 基于redis的API自行开发客户端 1.认识Redis Redis&#xff08;Remote Dictionary Serve…