最佳调度问题(回溯算法)
- 格式:doc
- 大小:182.54 KB
- 文档页数:5
算法设计与分析——批处理作业调度(回溯法)之前讲过⼀个相似的问题流⽔作业调度问题,那⼀道题最开始⽤动态规划,推到最后得到了⼀个Johnson法则,变成了⼀个排序问题,有兴趣的可以看⼀下本篇博客主要参考⾃⼀、问题描述给定n个作业的集合{J1,J2,…,Jn}。
每个作业必须先由机器1处理,然后由机器2处理。
作业Ji需要机器j的处理时间为t ji。
对于⼀个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。
所有作业在机器2上完成处理的时间和称为该作业调度的完成时间和。
批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度⽅案,使其完成时间和达到最⼩。
例:设n=3,考虑以下实例:看到这⾥可能会对这些完成时间和是怎么计算出来的会有疑问,这⾥我拿123和312的⽅案来说明⼀下。
对于调度⽅案(1,2,3)作业1在机器1上完成的时间是2,在机器2上完成的时间是3作业2在机器1上完成的时间是5,在机器2上完成的时间是6作业3在机器1上完成的时间是7,在机器2上完成的时间是10所以,作业调度的完成时间和= 3 + 6 + 10这⾥我们可以思考⼀下作业i在机器2上完成的时间应该怎么去求?作业i在机器1上完成的时间是连续的,所以是直接累加就可以。
但对于机器2就会产⽣两种情况,这两种情况其实就是上图的两种情况,对于(1,2,3)的调度⽅案,在求作业2在机器2上完成的时间时,由于作业2在机器1上还没有完成,这就需要先等待机器1处理完;⽽对于(3,1,2)的调度⽅案,在求作业2在机器2上完成的时间时,作业2在机器1早已完成,⽆需等待,直接在作业1被机器1处理之后就能接着被处理。
综上,我们可以得到如下表达式if(F2[i-1] > F1[i])F2[i] = F2[i-1] + t[2][i]elseF2[i] = F1[i] + t[2][i]⼆、算法设计类Flowshop的数据成员记录解空间的结点信息,M输⼊作业时间,bestf记录当前最⼩完成时间和,数组bestx记录相应的当前最佳作业调度。
回溯算法详解
回溯算法是一种经典问题求解方法,通常被应用于在候选解的搜索空间中,通过深度优先搜索的方式找到所有可行解的问题。
回溯算法的本质是对一棵树的深度优先遍历,因此也被称为树形搜索算法。
回溯算法的基本思想是逐步构建候选解,并试图将其扩展为一个完整的解。
当无法继续扩展解时,则回溯到上一步并尝试其他的扩展,直到找到所有可行的解为止。
在回溯算法中,通常会维护一个状态向量,用于记录当前已经构建的解的情况。
通常情况下,状态向量的长度等于问题的规模。
在搜索过程中,我们尝试在状态向量中改变一个或多个元素,并检查修改后的状态是否合法。
如果合法,则继续搜索;如果不合法,则放弃当前修改并回溯到上一步。
在实际应用中,回溯算法通常用来解决以下类型的问题:
1. 组合问题:从n个元素中选取k个元素的所有组合;
2. 排列问题:从n个元素中选择k个元素,并按照一定顺序排列的所有可能;
3. 子集问题:从n个元素中选择所有可能的子集;
4. 棋盘问题:在一个给定的n x n棋盘上放置n个皇后,并满足彼此之间不会互相攻击的要求。
回溯算法的时间复杂度取决于候选解的规模以及搜索空间中的剪枝效果。
在最坏情况下,回溯算法的时间复杂度与候选解的数量成指数级增长,因此通常会使用剪枝算法来尽可能减少搜索空间的规模,从而提高算法的效率。
总之,回溯算法是一种非常有用的问题求解方法,在实际应用中被广泛使用。
同时,由于其时间复杂度较高,对于大规模的问题,需要慎重考虑是否使用回溯算法以及如何优化算法。
最佳调度问题(搜索回溯)最佳调度问题【问题描述】假设有n个任务由k个可并⾏⼯作的机器完成。
完成任务i需要的时间为ti。
试设计⼀个算法找出完成这n个任务的最佳调度,使得完成全部任务的时间最早。
【编程任务】对任意给定的整数n和k,以及完成任务i需要的时间为ti,i=1~n。
编程计算完成这n个任务的最佳调度。
【输⼊格式】由⽂件machine.in给出输⼊数据。
第⼀⾏有2 个正整数n和k。
第2 ⾏的n个正整数是完成n个任务需要的时间。
【输出格式】将计算出的完成全部任务的最早时间输出到⽂件machine.out。
【输⼊样例】7 32 14 4 16 6 5 3【输出样例】17/*搜索问题我会先想每个⼩问题⾯临的共同条件是什么,每加⼊⼀件任务,他加⼊的条件是什么?他要加⼊哪个机器?加⼊的条件是什么?时间怎样最短?然后我就蒙圈了、、、我智障的想法是search(1,1)把第⼀个加⼊第⼀个机器,然后直到search(7,1)把第七个加⼊第⼀个,然后再加⼀个机器search(1,2)直到search(7,2)后来想想真是愚蠢呐,上⽹搜了⼀下、、才明⽩...orz,search(int,int)⾥⾯的两个参数⼀个表⽰加⼊的第⼏个任务,另⼀个是现在的时间,所以search()⾥⾯是什么很重要,我发现这就⽐如前⾯的题⽬的效率问题,求最⾼的效率,search()⾥⾯也有⼀个参数代表效率,从0开始;还有着⼀个题注意剪枝,当这⼀种情况已经⽐之前找到的最⼩值⼤时这种情况就可以舍弃不⽤考虑了QWQ*/#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;int a[30],s[30];int n,k,minn=0x7ffff;void search(int,int);int main() {scanf("%d%d",&n,&k);for(int i=1; i<=n; i++) //输⼊每个时间scanf("%d",&a[i]);search(1,0);//加⼊第⼀个时间,现在花费的时间是0;cout<<minn;return0;}void search(int x,int y) {if(y>=minn)return;//剪枝已经⽐最⼩的⼤了就不⽤再放了,舍弃这种⽅法if(x>n) { //放完了进⾏判断if(y<minn)minn=y;return;}for(int i=1; i<=k; i++) {if(s[i]+a[x]<=minn) { //剪枝s[i]+=a[x];search(x+1,max(y,s[i]));s[i]-=a[x];//回溯}}return;}。
1、批作业调度问题(1)问题描述给定n个作业的集合{J1,J2,…,Jn}。
每个作业必须先由机器1处理,然后由机器2处理。
作业Ji需要机器j的处理时间为tji。
对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。
所有作业在机器2上完成处理的时间和称为该作业调度的完成时间和。
批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小。
例:设n=3,考虑以下实例:这3个作业的6种可能的调度方案是1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1;它们所相应的完成时间和分别是19,18,20,21,19,19。
易见,最佳调度方案是1,3,2,其完成时间和为18。
(2)算法设计批处理作业调度问题要从n个作业的所有排列中找出具有最小完成时间和的作业调度,所以如图,批处理作业调度问题的解空间是一颗排列树。
按照回溯法搜索排列树的算法框架,设开始时x=[1,2,……n]是所给的n个作业,则相应的排列树由x[1:n]的所有排列构成。
算法具体代码如下:[cpp]view plain copy1.#include "stdafx.h"2.#include <iostream>ing namespace std;4.5.class Flowshop6.{7.friend int Flow(int **M,int n,int bestx[]);8.private:9.void Backtrack(int i);10.11.int **M, // 各作业所需的处理时间12. *x, // 当前作业调度13. *bestx, // 当前最优作业调度14.15. *f2, // 机器2完成处理时间16. f1, // 机器1完成处理时间17. f, // 完成时间和18.19. bestf, // 当前最优值20. n; // 作业数21. };22.23.int Flow(int **M,int n,int bestx[]);24.25.template <class Type>26.inline void Swap(Type &a, Type &b);27.28.int main()29.{30.int n=3,bf;31.int M1[4][3]={{0,0,0},{0,2,1},{0,3,1},{0,2,3}};32.33.int **M=new int*[n+1];34.35.for(int i=0;i<=n;i++)36. {37. M[i]=new int[3];38. }39. cout<<"M(i,j)值如下:"<<endl;40.41.for(int i=0;i<=n;i++)42. {43.for(int j=0;j<3;j++)44. {45. M[i][j]=M1[i][j];46. }47. }48.49.int bestx[4];50.for(int i=1;i<=n;i++)51. {52. cout<<"(";53.for(int j=1;j<3;j++)54. cout<<M[i][j]<<" ";55. cout<<")";56. }57.58. bf=Flow(M,n,bestx);59.60.for(int i=0;i<=n;i++)61. {62.delete []M[i];63. }64.delete []M;65.66. M=0;67.68. cout<<endl<<"最优值是:"<<bf<<endl;69. cout<<"最优调度是:";70.71.for(int i=1;i<=n;i++)72. {73. cout<<bestx[i]<<" ";74. }75. cout<<endl;76.return 0;77.}78.79.void Flowshop::Backtrack(int i)80.{81.if (i>n)82. {83.for (int j=1; j<=n; j++)84. {85. bestx[j] = x[j];86. }87. bestf = f;88. }89.else90. {91.for (int j = i; j <= n; j++)92. {93. f1+=M[x[j]][1];94.//机器2执行的是机器1已完成的作业,所以是i-195. f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+M[x[j]][2];96.97. f+=f2[i];98.if (f < bestf)//剪枝99. {100. Swap(x[i], x[j]); 101. Backtrack(i+1); 102. Swap(x[i], x[j]); 103. }104. f1-=M[x[j]][1];105. f-=f2[i];106. }107. }108.}109.110.int Flow(int **M,int n,int bestx[]) 111.{112.int ub=30000;113.114. Flowshop X;115. X.n=n;116. X.x=new int[n+1];117. X.f2=new int[n+1];118.119. X.M=M;120. X.bestx=bestx;121. X.bestf=ub;122.123. X.f1=0;124. X.f=0;125.126.for(int i=0;i<=n;i++)127. {128. X.f2[i]=0,X.x[i]=i;129. }130. X.Backtrack(1);131.delete []X.x;132.delete []X.f2;133.return X.bestf;134.}135.136.template <class Type>137.inline void Swap(Type &a, Type &b) 138.{139. Type temp=a;140. a=b;141. b=temp;142.}由于算法Backtrack在每一个节点处耗费O(1)计算时间,故在最坏情况下,整个算法计算时间复杂性为O(n!)。
1. )(n T 给定数组a[0:n-1],试设计一个算法,在最坏情况下用n+[logn]-2次比较找出a[0:n-1] 中的元素的最大值和次大值. (算法分析与设计习题 2.16 ) (分治法)a 、 算法思想用分治法求最大值和次大值首先将问题划分,即将划分成长度相等的两个序列,递归求出左边的最大值次大值,再求出右边的的最大值次大值,比较左右两边,最后得出问题的解。
b 、复杂度分析:把问题划分为左右两种的情况,需要分别递归求解,时间复杂度可如下计算:有递推公式为:T(n)=1 n=1T(n)= 2T(n/2)+1 n>1所以,分治算法的时间复杂度是n+[logn]-2,当n 为奇数时,logn 取上线,当n 为偶数时,logn 取下线。
//不知道为什么会-2!C 、代码实现:#include <stdio.h>int a[100]; void maxcmax(int i,int j,int &max,int &cmax){int lmax,lcmax,rmax,rcmax;int mid;if (i==j){ max=a[i];cmax=a[i];}else if (i==j-1)if (a[i]<a[j]){max=a[j];cmax=a[i];}else{max=a[i];cmax=a[j];}else{mid=(i+j)/2;maxcmax(i,mid,lmax,lcmax);maxcmax(mid+1,j,rmax,rcmax);if(lmax>rmax)if(lcmax>rmax){max=lmax;。
cmax=lcmax;}else{max=lmax;cmax=rmax;}elseif(rcmax>lmax){if(rmax==rcmax){max=rmax;cmax=lmax;}else{max=rmax;cmax=rcmax;}}。
算法实现题3-7 数字三角形问题问题描述:给定一个由n行数字组成的数字三角形,如图所示。
试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
编程任务:对于给定的由n行数字组成的数字三角形,编程计算从三角形的顶至底的路径经过的数字和的最大值。
数据输入:有文件input.txt提供输入数据。
文件的第1行是数字三角形的行数n,1<=n<=100。
接下来的n行是数字三角形各行的数字。
所有数字在0-99之间。
结果输出:程序运行结束时,将计算结果输出到文件output.txt中。
文件第1行中的数是计算出的最大值。
输入文件示例输出文件示例 input.txt output.txt 5 30 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5源程序:#include "stdio.h" voidmain(){ intn,triangle[100][100],i,j;//triangle数组用来存储金字塔数值,n表示行数 FILE *in,*out;//定义in,out两个文件指针变量in=fopen("input.txt","r");fscanf(in,"%d",&n);//将行数n读入到变量n中for(i=0;i<n;i++)//将各行数值读入到数组triangle中for(j=0;j<=i;j++)fscanf(in,"%d",&triangle[i][j]);for(int row=n-2;row>=0;row--)//从上往下递归计算for(int col=0;col<=row;col++)if(triangle[row+1][col]>triangle[row+1][col+1])triangle[row][col]+=triangle[row+1][col];elsetriangle[row][col]+=triangle[row+1][col+1];out=fopen("output.txt","w");fprintf(out,"%d",triangle[0][0]);//将最终结果输出到output.txt中 }算法实现题4-9 汽车加油问题问题描述:一辆汽车加满油后可行驶nkm。
实验目的:理解回溯法的原理,掌握调度问题的处理方法,实现最佳调度问题的回溯解决。
问题定义输入:1.任务数N2.机器数M3.随机序列长度t[i],其中t[i]=x表示第i个任务完成需要时间单位x,输出:1.开销时间besttime,表示最佳调度需要时间单位2.最佳调度序列bestx[],其中bestx[i]=x,表示将第i个任务分配给第x个机器执行。
实验思想解空间的表示:一个深度为N的M叉树。
基本思路:搜索从开始结点(根结点)出发,以DFS搜索整个解空间。
每搜索完一条路径则记录下besttime 和bestx[]序列开始结点就成为一个活结点,同时也成为当前的扩展结点。
在当前的扩展结点处向纵深方向移至一个新结点,并成为一个新的活结点,也成为当前扩展结点。
如果在当前的扩展结点处不能再向纵深方向扩展,则当前扩展结点就成为死结点。
此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点;直至找到一个解或全部解。
测试数据及结果本测试的硬件以及软件环境如下CPU:PM 1.5G; 内存:768M;操作系统:windows xp sp2;软件平台:JDK1.5;开发环境:eclipse如图1所示:即为求任务数为10机器数为5的最佳调度的算法结果。
图1实验结论以及算法分析通过测试证明算法正确有效。
性能分析的方法:使用JDK 1.5的System.nanoTime(),计算算法消耗的时间,以此来评价算法。
(该方法在JDK1.5以下的版本中不支持)为了不影响算法的准确度,在测试的过程我们注释掉了打印随机字符串的步骤。
由于没有使用限界函数进行优化,算法时间和空间复杂度呈指数级增长。
所以该算法不适合较大规模的计算。
图2图2 蓝线表示机器数一定M=3时,n增大时求解最佳调度对所消耗的时间,该趋势随着指数增加。
图3图3表示任务数N一定时随着M的增大的增长曲线。
图2和图3表明该程序的时间复杂度与理论分析相符合。