• 分支界线与 回溯法


    分支限界法
    一、基本描述
    类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

    (1)分支搜索算法
    所谓“分支”就是采用广度优先的策略,依次搜索E-结点的所有分支,也就是所有相邻结点,抛弃不满足约束条件的结点,其余结点加入活结点表。然后从表中选择一个结点作为下一个E-结点,继续搜索。

    选择下一个E-结点的方式不同,则会有几种不同的分支搜索方式。

    1)FIFO搜索

    2)LIFO搜索

    3)优先队列式搜索

    (2)分支限界搜索算法
    二、分支限界法的一般过程
    由于求解目标不同,导致分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。

            分支限界法的搜索策略是:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展对点。为了有效地选择下一扩展结点,以加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解。

    分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树和排列树。在搜索问题的解空间树时,分支限界法与回溯法对当前扩展结点所使用的扩展方式不同。在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,那些导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被子加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所求的解或活结点表为空时为止。

             回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

    在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。

    2.操作
    也就是说解决一个回溯问题,实际上就是一个决策树的遍历过程。在这个过程中只需要思考三个问题:
    (1)路径:也就是已经做出的选择;
    (2)选择列表:也就是你当前可以做的选择;
    (3)结束条件:也就是1到达决策树底层,无法再做选择的条件
    回溯算法框架:

    result = []
      def backtrack(路径, 选择列表):
        if 满足结束条件:
          result.add(路径)
          return

        for 选择 in 选择列表:
          做选择
          backtrack(路径, 选择列表)
          撤销选择

    核心是for循环里面的递归,在递归调用之前做选择,在递归调用之后撤销选择。

    三、回溯法和分支限界法的一些区别
    有一些问题其实无论用回溯法还是分支限界法都可以得到很好的解决,但是另外一些则不然。也许我们需要具体一些的分析——到底何时使用分支限界而何时使用回溯呢?

    回溯法和分支限界法的一些区别:

    方法对解空间树的搜索方式 存储结点的常用数据结构 结点存储特性常用应用

    回溯法深度优先搜索堆栈活结点的所有可行子结点被遍历后才被从栈中弹出找出满足约束条件的所有解

    分支限界法广度优先或最小消耗优先搜索队列、优先队列每个结点只有一次成为活结点的机会找出满足约束条件的一个解或特定意义下的最优解
    ————————————————

    递归与分治概述:

    1、分治方法在于分和治。将一定规模的问题划分成性质相同的若干个小问题,分;对于每个小问题,进行所需要进行的操作,如排序等。

    2、关于递归:

    (1)递归中需要的成分是递归边界和递归规则,没有递归边界递归无法停止、无法进行。

    (2)通过将各层的关系从小到大逐渐带入,可以求解出一个函数的非递归表达式。

    (3)n该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

    3、Ackerman函数:

    Ackerman函数的定义为:

    根据关系可以递推出来:

    在m取不同的值的过程中,该函数的递推关系式也不同。只是一种无法用单一的非递归关系表达的函数。

    4、在Ackerman函数中,定义它的逆拟函数α(n)为使A(x)>n成立的最小的x。

    5、整数划分问题:

    对于整数的不同划分,考虑以下几种不同的情况:(1)m>n:q(n,m)=q(n,n)

    (2)m=n:q(n,m)=1+q(n,m-1)

    (3)m<n:q(n,m)=w(n,m)+q(n,m-1)

    又:w(n,m)=q(n-m,m)

    (4)m=1:q(n,1)=1

    6、汉诺塔问题:

    思路:将前n-1个元素移动到c上,然后将第n个移动到b上,再将c上的n-1个移动到b上。

    注意边界条件是:如果只有一个元素,直接move到b上。

    void Move(int no,char a,char b){
    printf("Move %d from %c to %c.\n",no,a,b);
    }
    void Hanoi(int n,char a,char b,char c){
    if(n==1){
    Move(1,a,b);
    }
    else{
    Hanoi(n-1,a,c,b);
    Move(n,a,b);
    Hanoi(n-1,c,b,a);
    }

    }
    7、分治法的基本思想:将一个叫难解决的大问题分成k个规模较小的子问题,逐个解决,分而治之。如果问题的规模还不够小,就继续划分,直到可以直接解决这个小问题。将求出的小问题自下而上的合并,最终得到问题的解

    8、递归的概念:

    递归算法:直接或间接地调用自身的算法。

    递归函数:用函数自身给出定义的函数。

    使用递归技术使得算法的描述简捷且易于理解

    10、分治法满足的要求:

    (1)问题的规模缩小到一定的程度就可以解决

    (2)该问题可以分成若干个规模较小的子问题

    (3)该问题分解出的子问题的解可以合并成为问题的解

    (4)问题没有重叠的子问题

    贪心算法(Greedy Alogorithm)又叫登山算法,它的根本思想是逐步到达山顶,即逐步获得最优解,是解决最优化问题时的一种简单但是适用范围有限的策略。
    贪心算法没有固定的框架,算法设计的关键是贪婪策略的选择。贪心策略要无后向性,也就是说某状态以后的过程不会影响以前的状态,至于当前状态有关。
    贪心算法是对某些求解最优解问题的最简单、最迅速的技术。某些问题的最优解可以通过一系列的最优的选择即贪心选择来达到。但局部最优并不总能获得整体最优解,但通常能获得近似最优解。
    在每一部贪心选择中,只考虑当前对自己最有利的选择,而不去考虑在后面看来这种选择是否合理。

    贪心算法求解事应考虑的问题
    1.候选集合S
    为了构造问题的解决方案,有一个候选集合C作为问题的可能解,问题的最终解均取自于候选集合C。
    2.解集合S
    随着贪心选择的进行,解集合不断扩展,直到构成一个满足问题的完整解。
    3.解决函数solution
    检查解集合是否构成问题的完整解。
    4.选择函数select
    即贪心策略,这是贪心算法的关键,它指出哪个候选对象有希望构成成问题的解。
    5.可行函数feasible
    检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。

    贪心算法的基本步骤
    1.从问题的某个初始解出发
    2.采用循环语句,党可以向求解目标前进一步时,就根据局部最优策略,得到一个不分解,缩小问题的范围或规模。
    3.将所有的部分解综合起来,得到问题的最终解。

    Greedy(C){//C是问题的输入集合即候选集合
    S={};//初始解集合为空
    while(not solution(S)){//集合S没有构成问题的一个解
    x=select(C);//在候选集合C中做贪心选择
    if feasible(S,x);//判断集合加入x后的解是否可行
    S=S+{x};
    C=C-{s};
    }
    return S;
    }

    贪心策略选择
    贪心算法的原理是通过局部最优来达到全局最优,采用的是逐步构造最优解的方法。在每个阶段,都做出一个看上去最优的,决策一旦做出,就不再更改。

    要选出最优解可不是一件容易的事,要证明局部最优为全局最优,要进行数学证明,否则就不能说明为全局最优。
    很多问题表面上看来用贪心算法可以找到最优解,实际上却把最优解给漏掉了。这就像现实生活中的“贪小便宜吃大亏”。所以我们在解决问题的时候,一定要谨慎使用贪心算法,一定要注意这个问题适不适合采用贪心算法。

    贪心算法很多时候并不能达到全局最优,为什么我们还要使用它呢?

    因为在很多大规模问题中,寻找最优解是一件相当费时耗力的事情,有时候付出大量人力物力财力后,回报并不与投入成正比。在这个时候选择相对最优的贪心算法就比较经济可行了。有的问题对最优的要求不是很高,在充分衡量付出和回报后,选择贪心算法未尝不是一种不错的选择呢。
    ————————————————

    动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。

    1.动态规划算法基本求解步骤:

    (1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。

    (2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。

    (3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。

    (4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。

    一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。实际应用中可以按以下几个简化的步骤进行设计:

    (1)分析最优解的性质,并刻画其结构特征。

    (2)递归的定义最优解。

    (3)以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值

    (4)根据计算最优值时得到的信息,构造问题的最优解

    2.动态规划的要素

    (1)最优子结构:问题的最优解由相关子问题的最优解组合而成,并且可以独立求解子问题。

    3.动态规划算法的性质

    (1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

    (2)无后效性:即某阶段状态(定义的新子问题)一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与其以前的状态有关。

    举例:现在有状态A,B,C;并且他们转化的先后因果关系是:A->B->C

    a.状态A确定后不会再受到B和C状态决策的影响,只和状态A之前的状态有关。

    b.状态B的确定是由状态A转化而来,B状态确定后不再受C状态的影响。

    c.状态C确定是由状态A和B转化而来。

    【注】无后效性的另一种重要的定义:无后效性是指如果在某个阶段上过程的状态已知,则从此阶段以后过程的发展变化仅与此阶段的状态有关,而与过程在此阶段以前的阶段所经历过的状态无关。

    举例:依然用上面的A,B,C三个状态举例:

    a.状态A确定后,可以转化为状态B

    b.状态B确定后,状态C可以由状态B转化而来,但是【状态C和状态A无关!只是有状态B转化而来的】。

    (3)有重叠子问题:即子问题之间是不独立的(分治法是独立的),一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

  • 相关阅读:
    skywalking
    数据库中redoundo的介绍
    grafana
    查看linux的ssh服务信息及运行状态
    获取当前路由
    使VM可以用内网ip访问
    sentinel
    一,安装dotnet
    数据模型(Data Model)
    Vue——获取对象类型
  • 原文地址:https://www.cnblogs.com/klb561/p/16212323.html
Copyright © 2020-2023  润新知