分支界定法 branch-and-bound 分析与实现)(转载)

1. 介绍分支界定法之前需要了解一下广度优先搜索breadth-First-search(BFS)

1、从图中某个顶点V0出发,并访问此顶点;以层为顺序,一层一层往下遍历

2、从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点

宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

BFS,其英文全称是Breadth First Search。 BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中。一般的实验里,其邻居节点尚未被检验过的节点会被放置在一个被称为 open 的容器中(例如队列或是链表)list, vector或者是最优值的小根堆大根堆,而被检验过的节点则被放置在被称为 closed 的容器中。(open-closed表)

广度搜索的判断重复如果直接判断十分耗时,我们一般借助哈希表来优化时间复杂度

分支界定法一般是广度优先搜索的优化

2. 分支界定法

分支界定算法是一种在问题的解空间树上搜索问题的解方法。一般如果有n层, 那么解空间树的叶节点有2 power n, 那么分支界定法就是使用限定条件,不断对这颗解空间树进行剪枝, 直到最后一层。

分支界定法采用广度优先或者最小耗费优先的方法搜索解空间树。 并且,在分解界定法中, 每一个活结点只有一次机会会成为扩展节点。他的搜索策略是:

1. 产生当前扩展节点的所有孩子节点, 并且在活结点链表中删除当前拓展节点;对于一般的求最优值和资源问题是二叉树, 对于求最短路径这样的图论问题, 是一般的树

2. 在产生的孩子节点中,剪掉那些不可能产生可行解(或最优解)的节点;(使用限定条件)

3.将其余所有的孩子节点加入活节点链表;

4.从活结点表中选择下一个活结点作为新的扩展节点;

5. 遇到层结束标记(采取不同的数据结构实现可能不同, 加入新的结束标记)

如此循环,直到找到问题的可行解(或最优解)或者活结点表为空。

分支界定法的思想是:首先确定目标值的上下界, 边搜索边减掉搜索树的某些枝, 提高搜索效率。

分支界定法在两个方面加速了算法的搜索效率, 一是在选择扩展节点时, 总是选择一个最小成本的节点, 尽可能早的进入最有可能成为最优解的分支;二是扩展节点的过程,舍弃导致不可行解或导致非最优解的子节点。

选择下一个扩展节点E-节点有如下方式

FIFO搜索

LIFO搜索

优先队列式搜索

举个例子,一个容量为10的集装箱,有重量分别为4,8,5的货物,如何才能装最多

3. 使用C++实现FIFO的分支界定算法, 集装箱问题

现在有一个集装箱, 能装30的容量, 有分别为10, 15, 20的货物, 问如何能最大化集装箱的载重货物??

我们知道答案为30

下面使用C++实现改算法

 

  1. #include <iostream>
    #include <list>  
    
    using namespace std;
    #define MAX 3  
    
    const int CAP=30;//最大容量
    const int box[MAX]={10,15,20};//三个箱子
    int main()
    {
        int temp=0,level=-1,best=0;
        int curVal=0,parentVal=0,expectVal=0;
        list<int> queue;
        queue.push_back(-1);//-1表示一层
        queue.push_back(parentVal);
        do
        {
            parentVal=queue.front(); //first in first out
            queue.pop_front(); //按照FIFO取出第一个元素,并且删除
            if(parentVal!=-1)
            {
                //left child
                curVal=parentVal+box[level]; //提高效率的一方面
                if(curVal>best&&curVal<=CAP)//边界条件 记录一个最大值 并且条件限定在CAP之内, 不满足条件的剪枝
                {
                    best=curVal;
                  std::cout<<"bestvalue"<<best<<endl;
                    //最后一层节点不加入队列
                    if(level<MAX-1)
                    {
                        queue.push_back(curVal);
                    }
                }  
    
                //right child
                temp = 0;
                curVal=parentVal;
                for(int i=level+1;i<MAX;i++)
                {
                    temp+=box[i];
                }
                expectVal=curVal+temp; //期望值为不加上当前level的值, 剩下的可能最大的值,提高效率的第二个方面
               std::cout<<"expect : "<<expectVal<<endl;
                //预计最大值若大于目前最佳值,则加入队列;否则不加入,即剪枝
                if(expectVal>best&&level<MAX-1)
                {
                    queue.push_back(curVal);
                }
                for(list<int>::iterator ite = queue.begin(); ite != queue.end(); ite++)
                    {
                        std::cout<<" "<<*ite;
                    }  
    
            }
            else//处理层次问题,加入层结束标志,进入下一层
            {
                if(level<MAX-1)
                {
                    queue.push_back(-1);
                }
                level++;
            }  
    
        }while(level!=MAX&&queue.empty()!=true);//到最后一层或者是队列为空结束  
    
        std::cout<<best<<endl;
        system("pause");
        return 0;
    }  

如果细分,上面的算法实际上是FIFO通过加入“限界”策略加速搜索。

搜索顺序仍采用FIFO的分支搜索,但当遇到:

若当前分支的“装载上界”,比现有的最大装载小,则该分支就无需继续搜索。

“装载上界”,容易求解,就是假设装载当前物品以后的所有物品。

4. FIFO实现分支搜索

有两艘船,n个货箱。第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱i的重量,且

w1+w2+……+wn≤c1+c2。

确定是否有一种可将所有n 个货箱全部装船的方法。若有的话,找出该方法。

1) 使用FIFO分支搜索实现算法

 

#include <iostream>
#include <queue>
using namespace std;
int w[100];
int n;
int bestw=0;
queue<int> queue1;//存储当前或节点
void AddLiveNode(int wt,int i)
{
    if (i==n)//是叶子, 需要结束
    {
        if (wt>bestw)
            bestw=wt;
    }
    else             //不是叶子
    {
        queue1.push(wt);
    }
}  

int MaxLoading(int c)
{
    // 初始化活结点队列,标记分层
    queue1.push(-1);
    int level=0;
    int currentw=0;
    while(!queue1.empty())
    {
        if(currentw!= -1)
        {
            if(currentw+w[level]<=c)   //左节点,限定条件为current + wi < c1
            {
                AddLiveNode(currentw+w[level],level);  //物品i可以装载
            }
            AddLiveNode(currentw,level); //右孩子总是可行的,不装载物品i
            //取下一个E-结点
            currentw = queue1.front();
            queue1.pop();
            cout<<"current"<<currentw<<endl;
        }
        else  //到达层的尾部
        {
            if (queue1.empty())
            {
                cout<<"best"<<bestw<<endl;
                return bestw;
            }
            //添加分层标记
            queue1.push(-1);
           //取下一个E-结点
            currentw = queue1.front();
            queue1.pop();
            cout<<"current next level"<<currentw<<endl;
            level++;
        }     //ew的
    }
}  

void main( )
{
    int c1,c2;int sum=0;
    scanf("%d", &c1);
    scanf("%d", &c2);
    scanf("%d", &n);
    cout<<"n"<<n<<endl;
    for(int i=0;i<n;i++)
    {
        scanf("%d", w+i);
        sum=sum+w[i];
    }
    if (sum<=c1 || sum<=c2)
   {
       cout<<"need only one ship"<<endl;
       return;
   }
   if (sum>c1+c2)
   {
       cout<<"no solution"<<endl;
       return;
   }
   MaxLoading(c1);
   if ((sum-bestw) <= c2)
   {
       cout<<"The first ship loading"<<bestw<<endl;
       cout<<"The second ship loading"<< sum-bestw<<endl;
   }
   else
   {
       cout<<"no solution"<<endl;
   }
   system("pause");
}  

5. 优先队列构造最优解

数据结构设计:

1)要输出解的方案,在搜索过程中仍需要生成解结构树,其结点信息包括指向父结点的指针和标识物品取舍(或是父结点的左、右孩子)。 (左节点1, 右节点0)

2)堆结点包括结点优先级信息:结点所在分支的装载上界uweight;堆中无法体现结点的层次信息(level),只能存储在结点中;

AddLiveNode用于把活结点加到子树中,并把HeapNode类型的活结点插入最大堆。

3)不同于算法2,由于扩展结点不是按层进行的,计算结点所在分支的装载上界时(expectVal>best),要用数组变量r记录当前层以下的最大重量,这样可随时方便使用各层结点的装载上界。

6. 算法复杂度

不管如何算法的复杂度仍为O(2n),但通过限界策略,并没有搜索子集树中的所有结点,且由于每次都是选取最接近最优解的结点扩展,所以一旦搜索到叶结点作E结点时算法就可结束。算法结束时堆并不一定为空。

转载自:http://blog.csdn.net/yinlili2010/article/details/39313035

时间: 2024-12-23 08:12:44

分支界定法 branch-and-bound 分析与实现)(转载)的相关文章

Branch and Bound:分支限界算法

http://blog.sciencenet.cn/blog-509534-728984.html 分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法.但与回溯算法不同,分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树,并且,在分支定界算法中,每一个活结点只有一次机会成为扩展结点. 利用分支定界算法对问题的解空间树进行搜索,它的搜索策略是: 1 .产生当前扩展结点的所有孩子结点: 2 .在产生的孩子结点中,抛弃那些不可能产生可行解(或最优解)的

Androidbaseadapterhelper源码分析与扩展(转载)

Androidbaseadapterhelper源码分析与扩展 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/44014941,本文出自:[张鸿洋的博客] 本篇博客是我加入Android 开源项目源码解析分析的一篇文章,初次加入,所以选了个比较简单的库,如果你曾经看过Android 快速开发系列 打造万能的ListView GridView 适配器对本篇博客就不会太陌生, base-adapter-helper就是完成类似万能

Python基础知识进阶(五---2)----程序基本结构、简单分支、异常处理、三大实例分析、基本循环结构、通用循环构造方法、死循环\嵌套循环、布尔表达式

上一篇随笔写的内容有点多了,决定分成两节,不然自己看的时候也头疼. 三者最大实例: 分支结构可以改变程序的控制流,算法不再是单调的一步步顺序执行. 假设:以找出三个数字中最大者的程序设计为例. 输入:三个数值. 处理:三者最大算法 输出:打印最大值 计算机怎么能确定哪个是计算机输入的最大值呢? 策略1:通盘比较 将每个值与其他所有值比较以确定最大值. if x1>=x2 and x1>=x3: max = x1 elif x2>=x1 and x2>= x3: max = x2 e

分支预测(branch prediction)

记录一个在StackOverflow上看到一个十分有趣的问题:问题. 高票答案的优化方法: 首先找到罪魁祸首: if (data[c] >= 128) sum += data[c]; 优化方案使用位操作: int t = (data[c] - 128) >> 31; sum += ~t & data[c]; 正数右移31一定为0,负数右移31一定为-1.再取反进行求&(按位与),0与任何数的&为0,-1与任何数的&为数本身.这样就巧妙的避开分支预测了,可以

2.EasyUI学习总结(二)——easyloader分析与使用(转载)

本文转载自:http://www.cnblogs.com/haogj/archive/2013/04/22/3036685.html 使用脚本库总要加载一大堆的样式表和脚本文件,在easyui 中,除了可以使用通常的方式加载之外,还提供了使用 easyloader 加载的方式.这个组件主要是为了按需加载组件而诞生.什么情况下使用它呢? 你觉得一次性导入 easyui 的核心 min js 和 css 太大 你只用到 easyui 的其中几个组件 你想使用其中的一个组件,但是你又不知道这个组件依赖

详细分析Java中断机制-转载

1. 引言 当我们点击某个杀毒软件的取消按钮来停止查杀病毒时,当我们在控制台敲入quit命令以结束某个后台服务时……都需要通过一个线程去取消另一个线程正在执行的任务.Java没有提供一种安全直接的方法来停止某个线程,但是Java提供了中断机制. 如果对Java中断没有一个全面的了解,可能会误以为被中断的线程将立马退出运行,但事实并非如此.中断机制是如何工作的?捕获或检测到中断后,是抛出InterruptedException还是重设中断状态以及在方法中吞掉中断状态会有什么后果?Thread.st

【NS2】NS2机制浅显分析一下(转载)

[我在之前看的是以ping协议为实例来理解TclCL机制和分裂对象模型] 本文以channel实例的创建过程为例,试图说明ns2的分裂机制,请在阅读本文前阅读<The NS Manual>有关分裂机制章节,由于篇幅有限,作者能力有限,本文章不能分析得非常彻底,时间仓促,有不当之处请大家给予批评指正. 一.定义信道基类 定义channel的 C++类 #ns-2.31macchannel.h class Channel : public TclObject { public: Channel(v

热点推荐:秒杀系统架构分析与实战--转载

原文地址:http://developer.51cto.com/art/201601/503511.htm 互联网正在高速发展,使用互联网服务的用户越多,高并发的场景也变得越来越多.电商秒杀和抢购,是两个比较典型的互联网高并发场景.虽然我们解决问题的具体技术方案可能千差万别,但是遇到的挑战却是相似的,因此解决问题的思路也异曲同工. 1 秒杀业务分析 正常电子商务流程(1)查询商品:(2)创建订单:(3)扣减库存:(4)更新订单:(5)付款:(6)卖家发货 秒杀业务的特性(1)低廉价格:(2)大幅

Hadoop基于Protocol Buffer的RPC实现代码分析-Server端--转载

原文地址:http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.google.com/p/protobuf/)作为RPC的默认实现,原来的WritableRpcEngine已经被淘汰了.来自cloudera的Aaron T. Myers在邮件中这样说的“since PB can provide support for evolving protocols in a co