Python算法学习[2]—算法思想&实践演练

Python算法学习[2]—算法思想&实践演练
AdamCY888

    本系列Python算法学习博文,基于《Python算法详解-张玲玲》一书,编译环境为IDLE(Python 3.7 64-bit)。
    博主系统计学方向,在学习Python算法之前,已经掌握了Python编程基础、Python数据挖掘与分析等知识。

    算法思想有很多,例如枚举、递归、分治、贪心、试探法、动态迭代和模拟等。本文将详细讲解常用的算法思想的基本知识以及实践演练,从而理解并掌握这些算法思想的基本用法和核心知识。

一、枚举算法

    枚举算法也叫穷举算法,最大的特点就是在面对问题时会去尝试每一种解决办法。在进行归纳推理时,如果逐个考察了某类事件的所有可能情况,因而得出一般结论,那么这个结论是可靠的,这种归纳方法叫做枚举法。

1.1、枚举法基础

    枚举法思想:将问题的所有可能的答案一一列举,然后根据条件判断答案是否合适,保留合适的,丢弃不合适的。在 P y t h o n Python Python语言中,一般使用 w h i l e while while循环或者 i f if if语句实现。
    思路如下:
    (1)确定枚举对象、枚举范围和判断条件。
    (2)逐一列举可能的值,验证每个解是否是问题的解。
    实践步骤:
    (1)解的可能范围,不能遗漏,也要避免重复。
    (2)判断是否是真正解的办法。
    (3)使可能解的范围降至最小,以便提高解决问题的效率。

1.2、实践演练—24点游戏

    (1)项目背景:24点是一款经典的棋牌类游戏,要求4个数字的运算结果等于24。这个游戏用扑克牌容易展开。用一副扑克牌,去掉牌面为大小王以及 J Q K JQK JQK纸牌,剩下1到10这40张牌(用1替代 A A A)。任意重中抽取4张牌,运用加、减、乘、除、开方(高端局,这里暂不讨论)以及括号把牌面上的数字算成24,并且抽取的4张牌有且只有1次使用机会。

    (2)算法分析:
     a . a. a. 枚举出所有的牌面组合情况。先构建一个列表,列表包含四个元素,情况有:[1,1,1,1],[1,1,1,2]…[1,1,1,10],[1,1,2,1]…[10,10,10,10]。
     b . b. b. 枚举运算法则。构造3个方程式以及从四则运算中遍历3种符号,从4个符号中选取3种且可以重复,将3个方程和3个符号叠加,从而实现所有可能的组合情况,即所有方程。
     c . c. c. 判断分析。判断每一个方程运算结果是否为24(在编程中,使用方程结果减去24其差值是否小于 1 ∗ 1 0 − 10 = 0 1*10^{-10}=0 11010=0 来判断是否相等,之所以这么设置,是因为与计算机以二进制表示小数有关,当然,在这里也可以直接判断是否等于24),若满足条件则输出,否则跳过。

    (3)程序设计:利用枚举法,找到所有牌面符合24点游戏规则的组合情况。

#项目:24点游戏:1-10的数字中,通过四则运算使得结果为24(枚举算法)
#author:AdamCY888.
#横向滑动查看完整代码

import itertools
idx = 0
for a in range(1,11):
      for b in range(1,11):
            for c in range(1,11):
                  for d in range(1,11):
                        numbers = [a,b,c,d] #构造四个数字组
                        for ops in itertools.product('+-*/',repeat=3):#选择三个运算符,可重复
                        	  #以下三种方程形式
                              bds_1 = '({0}{4}{1}){5}({2}{6}{3})'.format(*numbers,*ops) 
                              bds_2 = '(({0}{4}{1}){5}{2}){6}{3}'.format(*numbers,*ops)
                              bds_3 = '{0}{4}({1}{5}({2}{6}{3}))'.format(*numbers,*ops)
                              #注意:bds_1\bds_2\bds_3,包罗了所有可能的情况,或者其他情况可以转换到这三种情况中。
                              for bds in [bds_1,bds_2,bds_3]:
                                    try :
                                          if abs(eval(bds)-24)<1e-10:
                                                idx += 1      #计数                                          
                                                ##print(bds) #查看具体情况,如:((1+1)+1)*8
                                    except ZeroDivisionError:
                                          continue
print(idx) #输出:12650

二、递归算法

    因为递归算法思想往往用函数形式来体现,所以递归算法需要预先编写功能函数。这些函数拥有独立的功能,能够实现解决某个问题的具体功能,当需要时直接调用即可。在本文中,将会详细介绍递归算法思想的基本知识。

2.1、递归法基础

    递归算法解决大多数问题十分有效,有如下三个特点:
    (1)递归过程一般通过函数或者子过程来实现。
    (2)递归算法在函数或者子过程的内部,直接或间接地调用自己的算法。
    (3)递归算法实际上是把问题转换为规模缩小地同类问题的子问题,然后递归调用函数或过程来表示问题的解,例如经典的斐波那契数问题。
    注意如下几点:
    (1)递归是在过程或者函数中调用自身的过程。在 P y t h o n Python Python程序设计时,定义一个函数可以调用其自身,例如:定义一个 d i g u i ( ) digui() digui()函数,在其函数内部通过判断,当符合条件时再次运用 d i g u i ( ) digui() digui()函数,否则就终止递归。

#递归示例
def digui(x,y): #定义函数,输入两个参数
      s = x+y
      if s < 100:
            s_1 = x*y
            s_2 = x/y
            digui(s_1,s_2) #调用本身
      else: #s >= 100 结束条件为递归出口
            print(s)
digui(3,7) #输出:441.18

    (2)使用递归策略时,必须有一个明确的递归结束条件,称之为递归出口。
    (3)递归算法通常显得很简洁,但运行效率较低,所以一般不提倡用递归算法设计程序。
    (4)在递归调用过程中,系统用栈来存储每一层的返回点和局部量。如果递归次数过多,则容易造成栈溢出,所以一般不提倡使用递归算法设计程序。

2.2、实践演练—汉诺塔顺序

    (1)项目背景:寺院有三根柱子,第一根柱子串有64个盘子,从上往下盘子越来越大且没有尺寸一样的盘子,另两根柱子没有串盘子。方丈要求小和尚把这64个盘子全部挪动到第三根柱子,但要求在移动的过程中始终是小盘子压着大盘子,而且每次只能移动一个盘子,那么小和尚应当如何挪动盘子?至少又需要挪动多少次盘子呢?

在这里插入图片描述

图1 三个盘子三根柱子的汉诺塔图形

    (2)挪动步骤次数分析:首先将盘子从上往下依次编号为1、2、3…64号,利用归纳法计算次数,并再用递归法设计程序。
     a . a. a. 假设有1号盘子,那么至少需要1步,柱子1→柱子3,“→”表示移动方向。
     b . b. b. 假设有2个盘子,那么至少需要3步,表1所示。

表1 两个盘子的汉诺塔顺序情况
步骤盘子编号起始位置终止位置
1112
2213
3123

    表1表示:第1步,1号盘子从柱子1→柱子2。第2步,2号从柱子1→柱子3。第3步:1号从柱子2→柱子3,共计3步。

     c . c. c. 假设有3个盘子,则至少需要7步如表2,表2解释规则如表1。

表2 三个盘子汉诺塔移动顺序情况
步骤盘子编号起始位置终止位置
1113
2212
3132
4313
5121
6223
7113

     d . d. d. 由上述 a a a, b b b, c c c情况,运用数学归纳法,推测盘子个数与移动次数存在变量关系为 y = 2 n − 1 , n = 1 , 2 , 3 , . . . y=2^{n}-1 ,n=1,2,3,... y=2n1,n=1,2,3,...,其中 y y y为移动次数, n n n为盘子个数。若推导式无误,那么当 n = 64 n=64 n=64时,则至少有 y = 2 64 − 1 y=2^{64}-1 y=2641个步骤。显然这是一个庞大的计算量,且不知道移动顺序,则需要借助计算机编程进行验证并给出移动顺序。

    (3)挪动步骤顺序分析
    在 a a a, b b b, c c c情况中,是通过思维进行推演,那么如何设计算法呢?这需要将汉诺塔问题拆分为3部分,理解为将柱子1的盘子借助柱子2挪动到柱子3。
     a . a. a. 2个盘子汉诺塔的第2步、3个盘子的汉诺塔第4步都是居于步骤的中间位置,实现的是将最大的盘子从柱子1挪动到柱子3。那么 n n n个盘子,的挪动过程的中间那一步为将 n n n号盘子从柱子1挪动到柱子3,并且易知道 n n n号盘子其挪动次数仅有1次。
     b . b. b. 挪动 n n n号盘子从柱子1到柱子3之前,需要将前 n − 1 n-1 n1号盘子从柱子1借助柱子3挪动到柱子2,将其看作一个单独的 n − 1 n-1 n1个盘子的汉诺塔问题,若需要实现这个问题,则又可以看作 n − 2 n-2 n2的汉诺塔问题,依次递归,直至最后一步,将1号盘子从柱子1挪开。
     c . c. c. 挪动 n n n号盘子从柱子1到柱子3 之后,需要将前 n − 1 n-1 n1个盘子从柱子2借助柱子1挪动到柱子3,这同挪动 n n n号盘子从柱子1到柱子3之前一样,看作汉诺塔的递归过程。
     d . d. d. 整个实现:前 n − 1 n-1 n1号盘子从柱1借助柱3移动到柱2,再将 n n n号盘子从柱1挪动到柱3,最后再将前 n − 1 n-1 n1号盘子从柱2挪动到柱3,结束。

    (3)程序设计:将汉诺塔问题拆分为3个部分,在挪动 n n n号盘子前后看成递归过程。

#项目:汉诺塔顺序,输入参数n,得到移动顺序(递归算法)
#author:AdamCY888

def hannuota(n,z_1,z_2,z_3): #
    if n == 1:
        print(z_1, '-->', z_3)
    else:
        hannuota(n-1,z_1,z_3,z_2)#a
        hannuota(1,  z_1,z_2,z_3)#b
        hannuota(n-1,z_2,z_1,z_3)#c
        
        '''
        a:柱1串有的n-1号盘子挪动到柱2
        b:柱1的n号盘子挪动到柱3
        c:柱2串有的前n-1号盘子,挪动到柱3

        '''
##      # 查看n=3时,汉诺塔顺序     
##hannuota(3,'z_1','z_2','z_3')
##      # 查看n=64时,汉诺塔顺序  
##hannuota(64,'z_1','z_2','z_3')

      # 查看n=4时,汉诺塔顺序
hannuota(4,'z_1','z_2','z_3')
'''
#输出:
#开始
z_1 --> z_2 #第1步,将柱1的顶层盘子挪动到柱2顶层
z_1 --> z_3 #第2步,将柱1的顶层盘子挪动到柱3顶层,以下同理
z_2 --> z_3 #第3步,....
z_1 --> z_2
z_3 --> z_1
z_3 --> z_2
z_1 --> z_2
z_1 --> z_3
z_2 --> z_3
z_2 --> z_1
z_3 --> z_1
z_2 --> z_3
z_1 --> z_2
z_1 --> z_3
z_2 --> z_3 
#完成,结束
'''

    从程序设计结果可知, n n n个盘子的汉诺塔问题至少有 y = 2 64 − 1 y=2^{64}-1 y=2641个步骤,验证了之前的推测,且在程序设计中调用 h a n n u o t a hannuota hannuota(64,‘z_1’,‘z_2’,‘z_3’)函数即可得到其顺序。
    值得一提的是,很多问题都同汉诺塔问题一样,虽然最后代码简短直观,但在编写代码之前需要大量的时间思考如何将现实问题转化为计算机问题,往往解决问题的思维逻辑比代码更重要,程序设计有助于研究分析,但其仅为一种工具。对于一个统计学方向的学习者来说,将现实问题用计算机进行描述的能力十分重要,或许这也是为什么要举办数学建模、统计建模、市场调查等大赛的原因。

三、分治算法

    分治算法即采用各个击破的方法,将一个规模为 N N N的问题分解为 k k k个规模较小的子问题,这些子问题相互独立且与原问题性质相同。只要求出zi问题的解,就可以得到原问题的解。

3.1、分治算法基础

    有时候在编程过程中,经常遇到要处理的数据相当多,求解过程比较复杂,直接求解又比较耗时的问题。在求解这类问题时,可以采用各个击破的方法。具体做法是:先把这个大问题拆解为几个小的子问题,找放求出这几个子问题后,再用合适的方法把它们组合成大问题的解。如果这些子问题还是比较大,可以继续把它们分解成更小的问题,以此类推,直至可以求解出解为止。这就是分治算法的思想。
    分治算法的一般步骤:
    (1)分解,将要解决的问题划分成若干个规模较小的同类问题。
    (2)求解,当子问题划分得足够小,用较简单的方法解决。
    (3)合并,按照原来问题的要求,将子问题的解逐层合并成原问题的解。

3.2、实践演练—数组中第 k k k小元素

    (1)项目背景:给出一组数据即数组,找出数组中第 k k k小的元素(第1小就表示该数组的最小值,第 k k k小元素,可理解为顺序统计量 X ( k ) X_{(k)} X(k)),并且不关心数组内部元素之间是否存在规律。

    (2)算法分析:先从数组中按照某种规律抽取一个元素 a a a,将该数组划分为2部分,或者理解为有重合的3部分,分别为: > a >a a组, ≤ a ≤a a组, = a =a a组,并假设 ≤ a ≤a a组包含元素有 m m m个元素即长度为 m m m
     a . a. a. 判断 a a a是不是第 k k k小的元素,若是则算法结束,否则就进行下一步。
     b . b. b. 判断第 k k k小的元素在 > a >a a组还是 ≤ a ≤a a组。
     c . c. c. 假设第 k k k小元素在 ≤ a ≤a a组,且包含元素有 m m m个,则在 ≤ a ≤a a组找出第 k k k小元素。
     d . d. d. 假设第 k k k小元素在 > a >a a组,且,则在 > a >a a组找出第 k − m − 1 k-m-1 km1小元素。
     e . e. e. 继续判断下去,直至最后找到一个 p p p元素,使得该元素为第 k k k小元素。

    (3)程序设计:

#项目:查找数组中第k小元素(分治算法)
#author:AdamCY888

def search(lis,k):#查找规则
    shu_1 = lis[-1] #定义一个初始初始的分组界限a
    #将数组拆分为两部分
    shu_2 = [i for i in lis[:-1] if i <= shu_1]
    shu_3 = [i for i in lis[:-1] if i >  shu_1]
    long = len(shu_2) #查看小于等于a组的长度
    #判断第k小元素具体位置
    if k == long: 
        return shu_1
    elif long < k:
        return search(shu_3,k-long-1) #调用了函数本身,又可以看作递归
    else:
        return search(shu_2,k)
if __name__ == '__main__': #主函数
    lis = [2,1,3,56,0.01,99,123,0.13] #数组
    k = 4 #更改数字,找第k小元素
    print("第%s小元素为:"%k,search(lis,k-1)) #调用函数并打印结果
    #输出:第4小元素为: 2

四、贪心算法

    贪心算法也称之为贪婪算法,它在求解问题时总想用当前看来是最好的办法来实现。这种算法思想不从整体最优上考虑问题,而仅仅考虑某种意义上的局部最优来求解问题。虽然它并不能得到所有问题的整体最优,但是面对范围相当广泛的许多问题时,能产生整体最优解或整体最优解的近似解。由此可见,贪心算法只是追求某个范围内最优,可以称之为“温柔的贪婪”。

4.1、贪心算法基础

    贪心算法从问题的某个初始解出发,逐步逼近给定的目标,以便尽快求解出最好的解。当达到算法的某一步不能再继续前进时,就停止算法,给出一个近似解。
    其存在的3个问题:
    (1)不能保证最后的解是全局最优。
    (2)不能用来求解最大解、最小解问题。
    (3)只能求满足某些约束条件的可行解的范围。
    贪心算法思路:
    (1)建立数学模型来描述问题。
    (2)把求解的问题分成若干个子问题。
    (3)对每一个子问题求解,得到子问题局部最优解。
    (4)把子问题的局部最优合并成原来问题的一个解。
    贪心算法的基本过程:
    (1)从问题的某一初始解出发。
    (2) w h i l e while while能向给定总目标前进一步。
    (3)求出可行解的一个解元素。
    (4)由所有解元素组合成问题的一个可行解。

4.2、实践演练—汽车加油次数

    (1)项目背景:一辆汽车加满油后可以行驶 n n n千米,途中有若干个加油站。那么,在哪些加油站停靠加油,可使得沿途加油次数最少。对于给定的 n n n千米和 k k k个加油站位置,计算最少的加油次数。

    (2)算法分析:为了便于理解,分为3步进行探讨。
     a . a. a. 一种特殊情况:若起点有一个加油站或者汽车出发时邮箱加满,行驶 n n n千米后,恰好有1个加油站,则加满油继续行驶, n n n千米后又恰好有1个加油站,依次类推,则行驶路程加油次数是固定的,且加油次数也是最少的。
     b . b. b. 另一种情况:现实生活中几乎没有 a a a 中所描述的情况,在不考虑备用油箱的情况下,基本上是在油箱即将耗空时寻找就近加油站,或者在还有一定油量时分析未来一段路程中是否能到达下个加油站。当然也有最糟糕的情况,即行驶到半路油空,等待救援,这种情况在程序设计中并不希望出现。
     c . c. c. 油箱即将耗空时,附近不一定有加油站,所以分析有一定油量时未来一段路程中是否能到达下个加油站就是关键问题。当存有一定油量时会经过多个加油站,那么在哪个加油站加油才能使得整个路程中加油次数最少呢?若相邻两个加油站之间的距离为 d ( d > n ) d(d>n) d(d>n),则必然行驶途中油空,需要等到救援,所以相邻加油站之间的距离应当不大于汽车出发时油满的最大行驶距离,即 d ≤ n d≤n dn,才能行驶完整个路程。

    (3)程序设计:设加满油后最大行驶100千米,需要行驶275千米,途中有5个加油站,从出发点到第1个加油站、 k k k个加油站之间、最后一个加油站到终点之间的距离分别为:40、65、20、90、20、40千米。判断在哪些加油站加油,至少要加几次油,见图2,程序设计如下。

#项目:行驶途中,汽车加油次数最少次数(贪心算法)
#author:AdamCY888

n = 100 #油满时行驶n千米
k = 5   #k个加油站
#起始点、k个加油站、终点之间距离
d = [40,65,20,90,20,40]
idx = 0 #加油次数
for i in range(k):#0,1,2,3,4
    if d[i] > n:
        print("油空,等待救援")
m = 0 #遇到第m个加油站
long = 0 #行驶路程
while m <= k: #循环条件
    #现在在起始点或者到达加油站,不加油状态下,计算已经行驶距离加上到下个加油站距离
    long += d[m]  
    if long >= n:  #如果到不了下个加油站
        idx += 1   #那么就在当前加油站,加油1次
        long = d[m] #加满油后到下个加油站要行驶的距离
        print('在第%s个加油站加满油'%m)
    m += 1 #出发去下一个加油站
print('加油总次数:%d'%idx)
    '''
    #输出:
    在第1个加油站加满油
    在第3个加油站加满油
    在第4个加油站加满油
    加油总次数:3
    '''

在这里插入图片描述

图2 途中加油情况示图

五、试探算法

    试探算法也叫回溯法,试探算法的处事方式比较委婉,它暂时放弃关于问题规模大小的限制,并将问题的候选解按照某种顺序逐一枚举和检验。当发现当前候选解不可能是正确的解时,就选择下一个候选解并进行判断。如果当前候选解除不满足问题规模要求外,能够满足所有其他要求,则继续扩大当前候选解的规模,并继续试探。如果当前候选解满足包括问题规模在内的所有要求,该候选解就是问题的一个解。在试探算法中,放弃当前候选解,并继续寻找下一个候选解的过程称之为回溯。扩大当前候选解的规模,并继续试探的过程称之为向前试探。

5.1、试探算法基础

    试探算法的基本步骤:
    (1)针对所给问题,定义问题的解空间。
    (2)确定易于搜索的解空间结构。
    (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
    为了求得问题的正确解,试探算法会委婉地试探各种可能地情况。在进行试探的过程中,一旦发现原来选择的假设情况是不正确的,立即会自觉地退回一步重新选择,然后继续向前试探,如此这般反复进行,直至得到解或证明无解时才停止。

5.2、实践演练—八皇后位置

    (1)项目背景:国际象棋棋盘为 8 × 8 8×8 8×8的方格。皇后为一种棋子身份,其行棋规则为:横走、竖走、斜走均可,但每次行棋只能选择其中一个方向,可前进、后退,每次可以走任意步数(1~7步),但不能越子行棋。现假设棋盘上只有8个皇后,每个皇后与其他7个皇后处于敌对状态,假设各个皇后之间地位相等,如何在棋盘上放置8个皇后,使得她们相安无事?那么这样的摆放方法又有多少种呢?

    (2)算法分析:八皇后问题就如同数独,相同的数字不能在同一行、同一列,并且八皇后问题比数独要求更多的是不能在同一斜线。由于各个皇后之间地位是相等的,所以只需要考虑各个摆放皇后的相对位置即可,并不需要调换两个位置上的皇后来区别摆放情况。
     a . a. a. 由于有8个皇后,棋盘为 8 × 8 8×8 8×8的方格,所以最后摆放位置必然为每一行各有一个皇后棋子,每列各有一个皇后。这样,我们就可以将8个皇后的位置标注出来,如同行列式的角标,按照行角标从小到大排序 ,取出列角标来表示8个皇后的位置,例如[8,4,1,3,6,2,7,5]表示:第1行第8列、第2行第4列、第3行第1列…。由于在python中一般用0为起始,则又可以用[7,3,0,2,5,1,6,4]表示。
     b . b. b. 试探算法将每行的可行位置入栈(关于栈的知识将会在下一篇博文—Python算法学习[3]中讲解,就是放入数组 a a a[9],用的是 a a a[1]~ a a a[8]),不行就退栈换列重试,直到找到一套方案并输出。接着从第一行换列重试其他方案。这里借助八皇后问题—百度百科所提供的简洁代码。

    (3)程序设计:

#项目:八皇后问题
#参考:百度百科

def queen(A, cur=0):
    if cur == len(A):
        print(A)
        return 0
    for col in range(len(A)):  # 遍历当前行的所有位置
        A[cur] = col
        for row in range(cur):  # 检查当前位置是否相克
            if A[row] == col or abs(col - A[row]) == cur - row:
                break
        else:  # 如果完成了整个遍历,则说明位置没有相克
            queen(A, cur+1)  # 计算下一行的位置
queen([None]*8) #输出:[0, 4, 7, 5, 2, 6, 1, 3]等等,共计92种情况。

六、迭代算法

    迭代法也称为辗转法,是一种不断用变量旧值递推新值的过程,在解决问题时总是重复利用一种方法。与迭代法相对应的是直接法,即一次性的解决问题。迭代法又分为精确迭代和近似迭代。

6.1、迭代算法基础

    迭代算法是用计算机解决问题,需要做好3方面工作。
    (1)确定迭代变量。至少存在一个迭代变量,直接或者间接不断地由旧值地推出新值。
    (2)建立迭代关系式。迭代关系式是指如何从变量前一个值推到下一个值的公式或者关系,通常用递推或者倒推的方法来建立迭代关系式,迭代关系式的建立是解决迭代问题的关键。
    (3)对迭代过程进行控制。在编写程序时,必须确定在什么时候结束迭代过程,不能让迭代过程无休止的执行下去,这也是博主上一篇博文Python算法学习[1]—算法简介&数据结构 中提到的算法的有穷性。迭代过程又可以根据迭代次数分为两种情况,一种是迭代的次数是确定的,另一种迭代的次数是无法确定的,需要进一步分析出用来结束迭代过程的条件。

6.2、实践演练—非线性方程组

    (1)项目背景:非线性方程是指含有指数函数和余弦函数等非线性函数的方程,例如 e x − c o s ( π x ) = 0 e^{x}-cos(\pi x)=0 excos(πx)=0。与线性方程相比,无论是解的存在性,还是求解的计算公式,非线性方程都要比线性方程复杂得多,对于一般的线性方程 f ( x ) = 0 f(x)=0 f(x)=0,既无直接法,也无一定规律可循。

    (2)程序设计:线性方程组的具体案例请参考线性方程组的迭代算法python代码实现,非线性方程组求解方法,以 e x − c o s ( π x ) = 0 e^{x}-cos(\pi x)=0 excos(πx)=0为例,代码如下。

#非线性问题
#参考:《python算法详解—张玲玲》

import math  #为了使用cos函数

def takeStep(Xcur):
    Xnex=[0,0,0];
    Xnex[0]=math.cos(Xcur[1]*Xcur[2])/3.0+1.0/6
    Xnex[1]=math.sqrt(Xcur[0]*Xcur[0]+math.sin(Xcur[2])+1.06)/9.0-0.1
    Xnex[2]=-1*math.exp(-1*Xcur[0]*Xcur[1])/20.0-(10*math.pi-3)/60
    return Xnex
    
def initialize():
    X0=[0.1,0.1,-0.1]
    return X0

def ColculateDistance(Xcur,Xnew):
    temp=[Xcur[0]-Xnew[0],Xcur[1]-Xnew[1],Xcur[2]-Xnew[2]]    
    dis=math.sqrt(temp[0]*temp[0]+temp[1]*temp[1]+temp[2]*temp[2])
    return dis
    
def iteration(eps,maxIter):
    cur_eps=10000
    Xcur=initialize()
    Xnew=[0,0,0]
    iterNum=1
    print("--------------------------开始迭代--------------------------")
    print("  迭代次数  |    Xk1    |    Xk2    |    Xk3    |    eps    ")

    while (cur_eps>eps and iterNum<maxIter) :
        Xnew=takeStep(Xcur);
        cur_eps=ColculateDistance(Xcur,Xnew)
        print("     %d       %.8f  %.8f  %.8f  %8.8f"%(iterNum,Xcur[0],Xcur[1],Xcur[2],cur_eps))
        iterNum+=1
        Xcur=Xnew
    return 0

iteration(10**-10,200)

"""
#输出:
--------------------------开始迭代--------------------------
  迭代次数  |    Xk1    |    Xk2    |    Xk3    |    eps    
     1       0.10000000  0.10000000  -0.10000000  0.58923871
     2       0.49998333  0.00944115  -0.52310127  0.00941924
     3       0.49999593  0.00002557  -0.52336331  0.00023523
     4       0.50000000  0.00001234  -0.52359814  0.00001231
     5       0.50000000  0.00000003  -0.52359847  0.00000031
     6       0.50000000  0.00000002  -0.52359877  0.00000002
     7       0.50000000  0.00000000  -0.52359878  0.00000000
     8       0.50000000  0.00000000  -0.52359878  0.00000000
"""

七、小结

    在本文中介绍了枚举法、递归算法、分治算法、贪心算法、试探算法、迭代算法以及这六种算法的实践案例。枚举算法就是将问题可能的答案一一列举。递归算法是把问题转换为规模缩小的同类问题的子问题然后求解。分治算法是将规模较大的问题分成一定量的规模较小的子问题,这些问题之间相互独立与原问题性质相同。贪心算法从局部最优解出发来探讨问题。试探算法暂时放弃关于问题规模大小的限制,将问题的候选解按照某种顺序逐一枚举和检验。迭代算法是不断用变量的旧值递推新值的过程。
    最后,这些算法是解决问题的思维逻辑,并不限于固定的范式,在探讨一个问题的解决方法时可能会运用到单个、多个算法叠加,同时还需要考虑执行效率。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

高山莫衣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值