送你一份Python算法的打怪升级路线图 | 用 Python 实现各种常用算法

640?wx_fmt=png

今天推荐一个Python学习的干货。

对程序员来说,算法的重要性不言而喻,算法基础是否扎实,间接决定了你是月薪5000,还是月薪50000。实验楼上线了一门免费的Python算法入门课——【用 Python 实现各种常用算法】,主要包括两部分内容:一是各种算法的基本原理讲解,二是各种算法的代码实现,内容由浅入深,非常适合刚入门Python的同学学习。

算法的基本原理讲解部分,包括:栈、队列、链表、树、字典树、堆、并查集、哈希、斐波那契数列问题、最大公约数问题、线性代数、顺序搜索、二分搜索、插值搜索、跳跃搜索、快速搜索、冒泡排序、选择排序 、插入排序、希尔排序、归并排序、快速排序、桶排序等等。

除了介绍各种不同算法的原理,课程还给出了代码示例和课后作业,手把手教你学会这些算法,并在课后把知识消化成自己的。

不少知识还给出了示意图,以更初学者能够更直观的理解。你可以在PC浏览器中登录实验楼学习这门课程。

课程链接:https://www.shiyanlou.com/courses/1265

小程序在线查看:

Python 实现各种常用算法

下面是课程第三节的内容——《数学算法》

数学算法

简介

本节内容将介绍数学算法。主要包含数学上几个基本问题,算术分析方法,斐波那契数列问题,以及最大公约数问题。

知识点
  • 数学几个基本问题

  • 算术分析方法

  • 斐波那契数列问题

  • 最大公约数问题

数学上几个基本问题

在编程界,有这么几个经典的数学问题,这里给大家简单介绍下。

3n+1 问题

对于任意大于 1 的自然数 n,若该数为偶数则将其变为原来的一半,若为奇数则将其变为 3n+1。反复进行上述过程,直到结果为 1 时停止。这就是著名的“3n+1”问题。要求输入 n,输出按“3n+1”规则变换到 1 所需要的数字变换次数。(n<=10^9)

题目描述:

3n+1 问题是一个简单有趣而又没有解决的数学问题。这个问题是由 L. Collatz 在 1937 年提出的。克拉兹问题(Collatz problem)也被叫做 hailstone 问题、3n+1 问题、Hasse 算法问题、Kakutani 算法问题、Thwaites 猜想或者 Ulam 问题。克拉兹问题的特殊之处在于:尽管很容易将这个问题讲清楚,但直到今天仍不能保证这个问题的算法对所有可能的输入都有效——即至今没有人证明对所有的正整数该过程都终止。

问题如下:

  1. 输入一个正整数 n;

  2. 如果 n=1 则结束;

  3. 如果 n 是奇数,则 n 变为 3n+1,否则 n 变为 n/2;

  4. 转入第 2 步。

请大家编程,构造一个 3n+1 函数,该函数完成对 43 的 3n+1 的演化,建立一个列表储存演化过程,最终返回该列表及需要演化的步骤数。

例子:

input:7

output:[7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1] 16

在 /home/shiyanlou/下新建一个文件 3n+1.py

参考代码如下:

注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。

def function(num):	
    mylist=[num]	
    while num!=1:	
        if num%2==1: #如果为奇数	
            num=3*num+1	
            mylist.append(num)	
        else: #为偶数	
            num=num//2	
            mylist.append(num)	
    return mylist,len(mylist)-1	
print(function(43))
- name: check if myproject exist	
  script: |	
    #!/bin/bash	
    ls /home/shiyanlou/3n+1.py	
  error: 我们发现您还没有在 /home/shiyanlou 目录下新建 3n+1.py 文件

寻找最值问题

在诸多数学问题,最值问题一直是争论的主题。在求最值的探索的过程,如何更快更好地得到最值,一代又一代理论科学家做出很大的贡献,逐渐衍化出来很多排序算法。关于排序算法的知识点,我们将在接下来的章节里介绍,这里将只对最大值,最小值进行简单讨论。 最大值求解给定一个列表 [2, 4, 9, 7, 19, 94, 5],请编写 find_max 函数,该函数返回列表的最大值。这个函数很容易,相信大家一定能够自行编写出来。参考代码如下:
def find_max(nums):	
    max = nums[0]	
    for x in nums:	
      if x &gt; max:	
        max = x	
    print(max)	
def main():	
  find_max([2, 4, 9, 7, 19, 94, 5])	
if __name__ == '__main__':	
  main()
最小值求解定一个列表 [2, 4, 9, 7, 19, 94, 5],请编写 find_min 函数,该函数返回列表的最小值。这个函数也很容易,相信大家一定能够自行编写出来。参考代码如下:
def find_min(nums):	
    min=nums[0]	
    for x in nums:	
        if x&lt;min:	
            min=x	
    pritn(min)	
def main():	
  find_max([2, 4, 9, 7, 19, 94, 5])	
if __name__ == '__main__':	
  main()

最大公约数/最小公倍数

在介绍最大公约数和最小公倍数之前,我们先来介绍下欧几里得算法。 欧几里得算法欧几里得算法又称辗转相除法,用来求两个正整数的最大公约数。以 1997 和 615 为例,用欧几里得算法求解如下:1997=615*3+152615=152*4+7152=7*21+57=5*1+25=2*2+12=1*2+0当被加的数为 0 时,可以得出,1997 和 615 的最大公约数为 1。以上做法的依据是以下定理: 两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。用数学表示为:gcd(a,b)=gcd(b,amodb)并可以证明欧几里得算法一定能在有限步内结束。利用图形可以更直观地理解欧几里得算法 640?wx_fmt=gif程序设计: 640?wx_fmt=jpeg 图片来至于百度百科算法思想:a,b 始终扮演被除数和除数的角色。最大公约数(greatest common divisor)缩写为 gcd参考代码如下:
def gcd(a,b):	
    while b:	
        r=a%b	
        a=b #经过一次求余数,进入第二轮相除,即b的值扮演了被除数的角色	
        b=r #r的值扮演了余数的角色	
    return a #这里return a的原因是,b作为在最后一轮的除数已经幅值给了a
而对于最小公倍数则有以下思路求解:a,b 两个数的最小公倍数乘以他们的最大公约数约等于 a 和 b 本身的乘积。即 LCM(a,b)GCD(a,b)=ab所以最小公倍数的求解代码如下:
def lcm(a,b):	
    return a*b//gcd(a,b)
请大家编程,构造一个 three gcd 和 threelcm 函数,该函数完成三个数的最大公因数和最小公倍数的求解。例子:input:2,7,6output:gcd:1 lcm:84在  /home/shiyanlou/下新建一个文件  three_gcd_lcm.py。注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。思路:计算 三个数的最大公约数时,利用之前写好的计算 2 个数的最大公约数的方法,先算出 a,b 的公约数,再用 a,b 的公约数与 c 再代入方法,此时返回的值就是三个数的最大公约数了。对于 三个数的最小公倍数,继续嵌套,先求出两个数的,再求与第三个数的最小公倍数。参考代码如下:
def gcd(a,b):	
    while b:	
        r=a%b	
        a=b #经过一次求余数,进入第二轮相除,即b的值扮演了被除数的角色	
        b=r #r的值扮演了余数的角色	
    return a #这里return a的原因是,b作为在最后一轮的除数已经幅值给了a	
def lcm(a,b):	
    return a*b//gcd(a,b)	
def three_gcd(a,b,c):	
    return gcd(gcd(a,b),c)	
def three_lcm(a,b,c):	
    return lcm(a,b)*c//gcd(gcd(a,b),c)	
def main():	
    a=2	
    b=7	
    c=6	
    print("a:",a)	
    print("b:",b)	
    print("c:",c)	
    print("gcd(",a,",",b,",",c,"):",three_gcd(a,b,c))	
    print("lcm(",a,",",b,",",c,"):",three_lcm(a,b,c))	
if __name__=='__main__':	
    main()
- name: check if myproject exist	
  script: |	
    #!/bin/bash	
    ls /home/shiyanlou/three_gcd_lcm.py	
  error: 我们发现您还没有在 /home/shiyanlou 目录下新建 three_gcd_lcm.py 文件

算术分析方法

在数学问题中,有很多算术分析方法。包括二分法,牛顿递归等等。我们将在接下来的章节里,逐一介绍这两种方法。

二分法

二分法:对于区间[a,b]上连续不断且 f(a)·f(b)<0 的函数 y=f(x),通过不断地把函数 f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。注:定义来自百度百科。给定精确度 flex, 用二分法求函数 f(x) 零点近似值的步骤如下:

  1. 确定区间 [a,b],验证 f(a)*f(b)<0,给定精确度 flex



  2. 求区间 (a,b) 的中点 c



  3. 计算 f(c):
  • 如果 f(c)=0,则 c 就是函数的零点
  • 如果 f(a)*f(c)<0,则令 b=c
  • 如果 f(c)*f(b)<0,则令 a=c
  • 判断是否达到精确度 flex,即若 |a-b|< flex,则得到零点近似值 a(或 b),否则重复 2-3 步骤
例子:请在[1,1000]区间内,找到 x^3-2*x-5 的零点在  /home/shiyanlou/下新建一个文件  bisection.py。注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。
import math	
def bisection(function, a, b):  #指定函数function,指定区间[a,b]	
    start = a	
    end = b	
    if function(a) == 0:  #a即为零点	
        return a	
    elif function(b) == 0: #b即为零点	
        return b	
    elif function(a) * function(b) &gt; 0: #二分法无法在该区间找到零点	
        print("couldn't find root in [a,b]")	
        return	
    else: #f(a)*f(b)&lt;0 的情况	
        mid = (start + end) / 2	
        while abs(start - mid) &gt; 10**-7:  # 精确度设置为10**-7	
            if function(mid) == 0:	
                return mid	
            elif function(mid) * function(start) &lt; 0:	
                end = mid	
            else:	
                start = mid	
            mid = (start + end) / 2	
        return mid	
def f(x):	
    return math.pow(x, 3) - 2*x - 5	
if __name__ == "__main__":	
    print(bisection(f, 1, 1000))
- name: check if myproject exist	
  script: |	
    #!/bin/bash	
    ls /home/shiyanlou/bisection.py	
  error: 我们发现您还没有在 /home/shiyanlou 目录下新建 bisection.py 文件

牛顿法

牛顿递归原理在数值分析里面,牛顿法(亦称牛顿拉弗森法)是一种方法,用于查找更好的根(或零点)的例子寻根算法。牛顿法也被用于求函数的最值。由于函数取最值的点处的导数值为零,故可用牛顿法求导函数的零点,其迭代式为 640?wx_fmt=png关于牛顿法和拟牛顿法的区别,请参考牛顿法和拟牛顿法这篇回答。例子:请用牛顿法,求函数 x *3-2x-5 在 3 附近的零点。在  /home/shiyanlou/下新建一个文件  newton.py。参考代码如下:
def newton(function,function1,start):	
    #function 是 f(x) function1 是 f(x)的导函数,即f'(x)	
    x_n=start	
    while True:	
        x_n1=x_n-function(x_n)/function1(x_n) #递归式	
        if abs(x_n-x_n1) &lt; 10**-5:	
            return x_n1	
        x_n=x_n1	
def f(x):	
    return (x**3)-2*x-5	
def f1(x):	
    return 3*(x**2)-2	
if __name__=="__main__":	
    print(newton(f,f1,3))
- name: check if myproject exist	
  script: |	
    #!/bin/bash	
    ls /home/shiyanlou/newton.py	
  error: 我们发现您还没有在 /home/shiyanlou 目录下新建 newton.py 文件
问题升级:如果题目不提供导函数,只提供本函数表达式,但是提供一个合理区间,如何求解该区间内的零点?还是采用牛顿法的思路,导函数可以采用斜率不断递归的方式。 640?wx_fmt=gif在  /home/shiyanlou/下新建一个文件  intersection.py。参考代码如下:
def intersection(function,start1,start2):	
    #function 是 f(x),start1,start2 是区间端点	
    x_n=start1	
    x_n1=start2	
    while True:	
        x_n2=x_n1-(function(x_n1)/((function(x_n1)-function(x_n))/(x_n1-x_n))) # 递归式	
        if abs(x_n2-x_n1) &lt; 10**-5:	
            return x_n2	
        x_n=x_n1	
        x_n1=x_n2	
def f(x):	
    return (x**3)-2*x-5	
if __name__=="__main__":	
    print(intersection(f,3,3.5))
- name: check if myproject exist	
  script: |	
    #!/bin/bash	
    ls /home/shiyanlou/intersection.py	
  error: 我们发现您还没有在 /home/shiyanlou 目录下新建 intersection.py 文件

斐波那契数列问题

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34...在数学上,费波那契数列是以递归的方法来定义的:

  • F(0) = 0



  • F(1) = 1



  • F(n) = F(n - 1) + F(n - 2) (n >= 2)


注:定义来自百度百科。从数列上看,我们能看到这是一个典型的递归函数,python 递归方法实现。我们在这里将介绍斐波那契数列的 3 种用 python 方法实现。 1. 使用 python 递归方法实现斐波那契数列思路:f(n)=f(n-1)+f(n),我们直接使用 python 递归方法实现斐波那契数列。参考代码如下:
def fibo(num):	
    if num==1:	
        return 1	
    elif num==2:	
        return 1	
    elif num&gt;2:	
        return fibo(num-1)+fibo(num-2)	
    else:	
        print("False")
代码分析:递归函数特点,在函数内部调用自身本身;所以函数必须要求调用有底线,不能无穷向下调用,斐波那契数列的底线在 n=1 与 n=2 。 2. 使用 python 独特的列表实现斐波那契数列很多递归函数对于 python 来说非常简单,因为 python 有个独特的数据类型 list 列表,list 是动态的我们可以在尾部追加数据,这样一来斐波那契数列就是简单的加法游戏了。参考代码如下
def fibo(num):	
    if num==1:	
        return [1]	
    elif num==2:	
        return [1,1]	
    l=[1,1]	
    for i in range(2,num):	
        l.append(l[-2]+l[-1]) #将列表最后两项的值求和,将值添加到列表最后	
    return l	
print(fibo(10))
3. 使用 while 循环实现
def fibo(num):	
    a=1	
    b=1	
    i=0	
    l=[]	
    while i &lt; num:	
        l.append(a)	
        a,b=a+b,a	
        i=i+1	
    return l	
print(fibo(10))
关于斐波那契数列还可以用矩阵分解的思路,这里就不详述了,如果大家对矩阵分解的思路感兴趣,请自行探索。

数学仙境

在结束本节内容前,给大家展示下,其实还有很多有趣的东西可以用 python 显示。这是一个 Python 集合,用代码把数学算法渲染成一幅幅美丽的图像。Mandelbrot 集: 640?wx_fmt=jpeg李代数 E8 的根系: 640?wx_fmt=jpeg模群的基本域: 640?wx_fmt=jpeg双曲蜂巢: 640?wx_fmt=gif正二十面体万花筒: 640?wx_fmt=jpeg反应扩散方程模拟: 640?wx_fmt=gif120 胞腔: 640?wx_fmt=jpeg希望大家可以继续发掘 python 在生活中的美,原来代码也可以变的很浪漫!

总结

在数学算法这个章节,其实还有很多技巧与算法值得推敲和琢磨,例如光滑函数的积分问题,如何求模块化指数问题,如果大家对数学理论感兴趣,可以自行探索学习。回顾下本节内容主要包含了以下内容:
  • 数学几个基本问题
  • 算术分析方法
  • 斐波那契数列问题
  • 最大公约数问题
请把文档中所有的示例代码都手动完成并运行对比结果,只有这样才能更加熟练的掌握对应的相关知识。 640?wx_fmt=png

完整课程请移步实验楼学习—— 

PC端动手实验学习:

https://www.shiyanlou.com/courses/1265

小程序随时随地学习算法:


Python 实现各种常用算法


更多推荐 34 个送给 Java 程序员的练手项目合集 极度舒适的 Python 入门教程,佩奇也能学会~ 50个Python练手项目,拿去过冬吧! 10个精彩Python视频教程整理,10种新技能轻松Get!


640?wx_fmt=jpeg


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值