分枝限界法求解流水线作业调度问题

问题描述

有n个作业(编号为1~n)要在由两台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工,然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi(1≤i≤n)。
流水作业调度问题要求确定这n个作业的最优加工顺序,使得从第一个作业在机器M1上开始加工,到最后一个作业在机器M2上加工完成所需的时间最少。可以假定任何作业一旦开始加工,就不允许被中断,直到该作业被完成,即非优先调度。

问题求解

作业编号为1到n,调度方案的执行步骤为1到n,解空间每一层对应一个步骤的作业分配。
根结点对应步骤0(虚),依次为步骤1、2、…、n分配任务,叶子结点对应步骤n。
叶子节点为一个可行解,比较求最优解
对于按1~n顺序执行的某种调度方案,f1表示在M1上执行完当前第i步的作业对应的总时间,f2数组表示在M2上执行完当前第i步的作业的总时间。
若第i步执行作业j,计算公式如下:
f1=f1+a[j];
f2[i]=max(f1,f2[i-1])+b[j]
这里由于每个结点中都保存了f1和f2,因此可以将f2数组改为单个变量。将每个队列结点的类型声明如下:

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

对应的求结点e的lb的算法如下:

void bound(NodeType &e)	//求结点e的限界值	
{  int sum=0;
   for (int i=1;i<=n;i++)	//扫描所有作业
     if (e.y[i]==0) sum+=b[i];
				//仅累计e.x中还没有分配的作业的b时间
   e.lb=e.f2+sum;
}

用bestf(初始值为∞)存放最优调度时间。
bestx数组存放当前作业最优调度。
采用的剪枝原则是,仅仅扩展 e.lb<bestf 的结点。

代码

//问题表示
int n=4;			//作业数
int a[MAX]={0,5,12,4,8};	//M1上的执行时间,不用下标0的元素
int b[MAX]={0,6,2,14,7};	//M2上的执行时间,不用下标0的元素
//求解结果表示
int bestf=INF;			//存放最优调度时间
int bestx[MAX];		//存放当前作业最佳调度
int total=1;			//结点个数累计

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

void bound(NodeType &e)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        if(e.y[i]==0)//当前作业未被分配
            sum+=b[i];
    e.lb=e.f2+sum;
}

void bfs()
{
    NodeType e,e1;
    priority_queue<NodeType> qu;

    memset(e.x,0,sizeof(e.x));
    messet(e.y,0,sizeof(e.y));
    e.i=0;
    e.f1=0;
    e.f2=0;
    bound(e);
    e.no=total++;
    qu.push(e);

    while(!qu.empty())
    {
        e=qu.top();
        qu.pop();
        if(e.i==n)
        {
            if(e.f2<bestf)
            {
                bestf=e.f2;
                for(int j=1;j<=n;j++)
                    bestx[j]=e.x[j];
            }
        }

        e1.i=e.i+1;
        for(int j=1;j<=n;j++)
        {
            if(e.y[j]==1)
                continue;
            for(int i=1;i<=n;i++)
                e1.x[i]=e.x[i];
            e1.x[e1.i]=j;
            for(int i=1;i<=n;i++)
                e1.y[i]=e.y[i];
            e1.y[j]=1;
            e1.f1=e.f1+a[j];
            e1.f2=max(e1.f1,e.f2)+b[j];
            bound(e1);
            if(e1.lb<bestf)
            {
                e1.no=total++;
                qu.push(e1);
            }
        }
    }
}

改进

在扩展每个子结点时判断是否为叶子结点,若是则产生一个可行解,比较产生最优解,该叶子结点不进队;若不是叶子结点则将其进队。

//问题表示
int n=4;			//作业数
int a[MAX]={0,5,12,4,8};	//M1上的执行时间,不用下标0的元素
int b[MAX]={0,6,2,14,7};	//M2上的执行时间,不用下标0的元素
//求解结果表示
int bestf=INF;			//存放最优调度时间
int bestx[MAX];		//存放当前作业最佳调度
int total=1;			//结点个数累计

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

void bound(NodeType &e)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        if(e.y[i]==0)//当前作业未被分配
            sum+=b[i];
    e.lb=e.f2+sum;
}

void bfs()
{
    NodeType e,e1;
    priority_queue<NodeType> qu;

    memset(e.x,0,sizeof(e.x));
    messet(e.y,0,sizeof(e.y));
    e.i=0;
    e.f1=0;
    e.f2=0;
    bound(e);
    e.no=total++;
    qu.push(e);

    while(!qu.empty())
    {
        e=qu.top();
        qu.pop();

        e1.i=e.i+1;
        for(int j=1;j<=n;j++)
        {
            if(e.y[j]==1)
                continue;
            for(int i=1;i<=n;i++)
                e1.x[i]=e.x[i];
            e1.x[e1.i]=j;
            for(int i=1;i<=n;i++)
                e1.y[i]=e.y[i];
            e1.y[j]=1;
            e1.f1=e.f1+a[j];
            e1.f2=max(e1.f1,e.f2)+b[j];
            bound(e1);
            if(e1.i==n)//到达叶子节点
            {
                if(e1.f2<bestf)
                {
                    bestf=e1.f2;
                    for(int j=1;j<=n;j++)
                        bestx[j]=e1.x[j];
                }
            }
            else if(e1.lb<bestf)
            {
                e1.no=total++;
                qu.push(e1);
            }
        }
    }
}
  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
流水作业调度问题是指有n个作业需要在m台机器上完成,每个作业需要在一台机器上连续加工若干时间,每台机器同一时间只能加工一个作业,求完成所有作业的最短时间。分枝限界是一种求解最优化问题的算,可以用来解决流水作业调度问题。具体步骤如下: 1. 定义状态空间:将每个作业看作一个状态,每个状态包含两个属性:已经完成的作业和未完成的作业。 2. 定义状态扩展规则:对于每个状态,可以选择一个未完成的作业分配给一台机器进行加工,得到一个新的状态。 3. 定义状态评价函数:对于每个状态,计算已经完成的作业的总时间加上未完成的作业的最短时间估计值,作为该状态的评价函数。 4. 定义优先队列:将所有状态按照评价函数的大小排序,每次选择评价函数最小的状态进行扩展。 5. 进行状态扩展:对于选择的状态,按照所有未完成作业的顺序进行扩展,得到新的状态,并将新状态加入优先队列。 6. 剪枝:如果某个状态的评价函数大于当前最优解,则该状态可以被剪枝。 7. 循环执行步骤4-6,直到找到最优解或者队列为空。 以下是基于优先队列的分枝限界的流水作业调度问题的C++代码实现,供参考: <<引用>> --相关问题--: 1. 什么是流水作业调度问题? 2. 除了分枝限界,还有哪些算可以用来解决流水作业调度问题? 3.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值