算法论文分治法和分支限界Word文档格式.docx
- 文档编号:21972205
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:17
- 大小:150.44KB
算法论文分治法和分支限界Word文档格式.docx
《算法论文分治法和分支限界Word文档格式.docx》由会员分享,可在线阅读,更多相关《算法论文分治法和分支限界Word文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
运用分支限界对旅行商售货员问题进展算法设计,求解目标那么是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
〕分支限界法首先确定一个合理的限界函数,并根据限界函数确定目标函数的界;
然后按照广度优先策略遍历问题的解空间树,在某一分支上,依次搜索该结点的所有孩子结点,分别估算这些孩子结点的目标函数的可能取值〔对最小化问题,估算结点的下界,对最大化问题,估算结点的上界〕。
如果某孩子结点的目标函数值超出目标函数的界,那么将其丢弃〔从此结点生成的解不会比目前已得的更好〕,否那么入待处理。
关键词:
算法设计与分析;
分支限界法;
分治法
1分治法解决最近距离问题
1.1问题描述
集合S中有n个点,分治法的思想就是将S进展拆分,分为2局部求最近点对。
算法每次选择一条垂线L,将S拆分左右两局部为SL和SR,L一般取点集S中所有点的中间点的x坐标来划分,这样可以保证SL和SR中的点数目各为n/2,〔否那么以其他方式划分S,有可能导致SL和SR中点数目一个为1,一个为n-1,不利于算法效率,要尽量保持树的平衡性〕
依次找出这两局部中的最小点对距离:
δL和δR,记SL和SR中最小点对距离δ=min〔δL,δR〕,如图1.1:
图1.1
以L为中心,δ为半径划分一个长带,最小点对还有可能存在于SL和SR的交界处,如图1.1中的虚线带,p点和q点分别位于SL和SR的虚线围,在这个围,p点和q点之间的距离才会小于δ,最小点对计算才有意义。
1.2算法设计
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的一样问题,以便各个击破,分而治之。
分治策略是:
对于一个规模为n的问题,假设该问题可以容易地解决〔比方说规模n较小〕那么直接解决,否那么将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式一样,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
这种算法设计谋略叫做分治法。
如果原问题可分割成k个子问题,1<
k≤n,且这些子问题都可解并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。
由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。
在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。
这自然导致递归过程的产生。
分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
分治法在每一层递归上都有三个步骤:
分解:
将原问题分解为假设干个规模较小,相互独立,与原问题形式一样的子问题;
解决:
假设子问题规模较小而容易被解决那么直接解,否那么递归地解各个子问题;
合并:
将各个子问题的解合并为原问题的解。
分治法所能解决的问题一般具有以下几个特征:
1)该问题的规模缩小到一定的程度就可以容易地解决
2)该问题可以分解为假设干个规模较小的一样问题,即该问题具有最优子构造性质。
3)利用该问题分解出的子问题的解可以合并为该问题的解;
4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,那么可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的那么分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
算法的时间复杂度:
首先对点集S的点x坐标和y坐标进展升序排序,需要循环2nlogn次,复杂度为O(2nlogn)
接下来在分治过程中,对于每个S'
yL中的点,都需要与S'
yR中的6个点进展比拟
O(n)=2O(n/2)+(n/2)*6〔一个点集划分为左右两个点集,时间复杂度为左右两个点集加上中间区域运算之和〕
其解为O(n)<
O(3nlogn)
因此总的时间复杂度为O(3nlogn),比蛮力法的O(n2)要高效。
1.3算法实现
#include<
iostream.h>
math.h>
structPoint{
doublex;
doubley;
};
doubleClosestPoints(PointS[],intn)
{
inti,j,a=0,b=0,c=0;
intp=0,q=0;
doubledmin,k=99999.0,d1,d2,d,r[25],m,sum=0;
Pointtemp,S1[5],S2[5],P1[5],P2[5];
if(n<
2)
returnk;
for(i=0;
i<
n-1;
i++){
for(j=n-1;
j>
=i;
j--){
if(S[j].x<
S[j-1].x){
temp=S[j];
S[j]=S[j-1];
S[j-1]=temp;
}
n;
sum+=S[i].x;
m=sum/(float)n;
if(S[i].x<
m){
S1[a++]=S[i];
else
S2[b++]=S[i];
d1=ClosestPoints(S1,a);
d2=ClosestPoints(S2,b);
if(d1<
d2)
d=d1;
d=d2;
a;
if(abs(S1[i].x-m)<
d)
P1[p++]=S1[i];
b;
if(abs(S2[i].x-m)<
P2[q++]=S2[i];
p-1;
for(j=p-1;
if(P1[j].y<
P1[j-1].y){
temp=P1[j];
P1[j]=P1[j-1];
P1[j-1]=temp;
q-1;
for(j=q-1;
if(P2[j].y<
P2[j-1].y){
temp=P2[j];
P2[j]=P2[j-1];
P2[j-1]=temp;
dmin=abs(P2[0].y-P1[0].y);
q;
for(j=0;
j<
p;
j++){
if(abs(P2[i].y-P1[j].y)<
d){
r[c++]=sqrt((P1[i].x-P2[j].x)*(P1[i].x-P2[j].x)+(P1[i].y-P2[j].y)*(P1[i].y-P2[j].y));
dmin=r[0];
c;
if(r[i]<
dmin)
dmin=r[i];
if(d<
returnd;
returndmin;
voidmain(){
inti,n;
PointS[100];
cout<
<
"
请输入点的个数(小于等于100的正整数):
endl;
cin>
>
请输入平面上的"
n<
个点的横纵坐标:
i++)
S[i].x>
S[i].y;
平面上最近两个点间的距离为:
ClosestPoints(S,n)<
1.4运行结果与分析
图1.2问题1运行结果
输入4个不同点的坐标分别为[4,5],[6,1],[2,10],[7,8],输出平面上最近两个点的距离为4.47214。
根据多组测试的数据来看,分治法解决该问题得效率比蛮力法要高效的多,但是如果各子问题是不独立的那么分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
2分支限界解决旅行商售货员问题
2.1问题描述
某售货员要到假设干城市去推销商品,各城市之间的路程(或旅费)。
他要选定一条从驻地出发,经过每个城市一次,最后回到驻地的路线,使总的路程(或总旅费)最小。
路线是一个带权图。
如图1.3中各边的费用〔权〕为正数。
图1.3的一条周游路线是包括V中的每个顶点在的一条回路。
周游路线的费用是这条路线上所有边的费用之和。
旅行售货员问题的解空间可以组织成一棵树,从树的根结点到任一叶结点的路径定义了图的一条周游路线。
旅行售货员问题要在图1.3中找出费用最小的周游路线。
图1.3
2.2算法设计
分支限界法类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。
但在一般情况下,分支限界法与回溯法的求解目标不同。
回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标那么是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值到达极大或极小的解,即在某种意义下的最优解。
针对本问题我们可以得出:
1.利用二维数组保存图信息City_Graph[MAX_SIZE][MAX_SIZE],其中City_Graph[i][j]的值代表的是城市i与城市j之间的路径费用,一旦一个城市没有通向另外城市的路,那么不可能有回路,不用再找下去了
2.我们可以任意选择一个城市,作为出发点。
〔因为最后都是一个回路,无所谓从哪出发〕。
算法的根本思路:
首先考虑s=n-2的情形,此时当前扩展结点是排列树中某个叶结点的父结点。
如果该叶结点相应一条可行回路且费用小于当前最小费用,那么将该叶结点插入到优先队列中,否那么舍去该叶结点。
当s<
n-2时,算法依次产生当前扩展结点的所有儿子结点。
由于当前扩展结点所相应的路径是x[0:
s],其可行儿子结点是从剩余顶点x[s+1:
n-1]中选取的顶点x[i],且(x[s],x[i])是所给有向图G中的一条边。
对于当前扩展结点的每一个可行儿子结点,计算出其前缀(x[0:
s],x[i])的费用cc和相应的下界lcost。
当lcost<
bestc时,将这个可行儿子结点插入到活结点优先队列中,算法中while循环的终止条件是排列树的一个叶结点成为当前扩展结点。
当s=n-1时,已找到的回路前缀是x[0:
n-1],它已包含图G的所有n个顶点。
因此,当s=n-1时,相应的扩展结点表示一个叶结点,此时该叶结点所相应的回路的费用等于cc和lcost的值,剩余的活结点的lcost值不小于已找到的回路的费用,它们都不可能导致费用更小的回路。
因此已找到叶结点所相应的回路是一个最小费用旅行售货员回路,算法完毕时返回找到的最小费用,相应的最优解由数组v给出。
2.3算法实现
#include<
stdio.h>
istream>
usingnamespacestd;
//---------------------宏定义------------------------------------------
#defineMAX_CITY_NUMBER10//城市最大数目
#defineMAX_COST10000000//两个城市之间费用的最大值
//---------------------全局变量----------------------------------------
intCity_Graph[MAX_CITY_NUMBER][MAX_CITY_NUMBER];
//表示城市间边权重的数组
intCity_Size;
//表示实际输入的城市数目
intBest_Cost;
//最小费用
intBest_Cost_Path[MAX_CITY_NUMBER];
//最小费用时的路径
//------------------------定义结点---------------------------------------
typedefstructNode{
intlcost;
//优先级
intcc;
//当前费用
intrcost;
//剩余所有结点的最小出边费用的和
ints;
//当前结点的深度,也就是它在解数组中的索引位置
intx[MAX_CITY_NUMBER];
//当前结点对应的路径
structNode*pNext;
//指向下一个结点
}Node;
//---------------------定义堆和相关对操作--------------------------------
typedefstructMiniHeap{
Node*pHead;
//堆的头
}MiniHeap;
//初始化
voidInitMiniHeap(MiniHeap*pMiniHeap){
pMiniHeap->
pHead=newNode;
pHead->
pNext=NULL;
}
//入堆
voidput(MiniHeap*pMiniHeap,Nodenode){
Node*next;
Node*pre;
Node*pinnode=newNode;
//将传进来的结点信息copy一份保存
//这样在函数外部对node的修改就不会影响到堆了
pinnode->
cc=node.cc;
lcost=node.lcost;
pNext=node.pNext;
rcost=node.rcost;
s=node.s;
for(intk=0;
k<
City_Size;
k++){
x[k]=node.x[k];
}
pre=pMiniHeap->
pHead;
next=pMiniHeap->
pNext;
if(next==NULL){
pNext=pinnode;
else{
while(next!
=NULL){
if((next->
lcost)>
(pinnode->
lcost)){//发现一个优先级大的,那么置于其前面
pNext=pre->
pre->
break;
//跳出
pre=next;
next=next->
//放在末尾
//出堆
Node*RemoveMiniHeap(MiniHeap*pMiniHeap){
Node*pnode=NULL;
if(pMiniHeap->
pNext!
pnode=pMiniHeap->
pNext=pMiniHeap->
pNext->
returnpnode;
//---------------------分支限界法找最优解--------------------------------
voidTraveler(){
inti,j;
inttemp_x[MAX_CITY_NUMBER];
Node*pNode=NULL;
intminiSum;
//所有结点最小出边的费用和
intminiOut[MAX_CITY_NUMBER];
//保存每个结点的最小出边的索引
MiniHeap*heap=newMiniHeap;
//分配堆
InitMiniHeap(heap);
//初始化堆
miniSum=0;
for(i=0;
i++){
miniOut[i]=MAX_COST;
//初始化时每一个结点都不可达
j++){
if(City_Graph[i][j]>
0&
&
City_Graph[i][j]<
miniOut[i]){
//从i到j可达,且更小
miniOut[i]=City_Graph[i][j];
if(miniOut[i]==MAX_COST){//i城市没有出边
Best_Cost=-1;
return;
miniSum+=miniOut[i];
i++){//初始化的最优路径就是把所有结点依次走一遍
Best_Cost_Path[i]=i;
Best_Cost=MAX_COST;
//初始化的最优费用是一个很大的数
pNode=newNode;
//初始化第一个结点并入堆
pNode->
lcost=0;
//当前结点的优先权为0也就是最优
cc=0;
//当前费用为0〔还没有开场旅行〕
rcost=miniSum;
//剩余所有结点的最小出边费用和就是初始化的miniSum
s=0;
//层次为0
x[k]=Best_Cost_Path[k];
//第一个结点所保存的路径也就是初始化的路径
put(heap,*pNode);
//入堆
while(pNode!
=NULL&
(pNode->
s)<
City_Size-1){
//堆不空不是叶子
Best_Cost_Path[k]=pNode->
x[k];
//将最优路径置换为当前结点本身所保存的
/*
**pNode结点保存的路径中的含有这条路径上所有结点的索引
**x路径中保存的这一层结点的编号就是x[City_Size-2]
**下一层结点的编号就是x[City_Size-1]
*/
if((pNode->
s)==City_Size-2){//是叶子的父亲
intedge1=City_Graph[(pNode->
x)[City_Size-2]][(pNode->
x)[City_Size-1]];
intedge2=City_Graph[(pNode->
x)[City_Size-1]][(pNode->
x)[0]];
if(edge1>
=0&
edge2>
cc+edge1+edge2)<
Best_Cost){
//edge1-1表示不可达
//叶子可达起点费用更低
Best_Cost=pNode->
cc+edge1+edge2;
cc=Best_Cost;
lcost=Best_Cost;
//优先权为Best_Cost
s++;
//到达叶子层
else{//部结点
for(i=pNode->
s;
i++){//从当前层到叶子层
if(City_Graph[pNode->
x[pNode->
s]][pNode->
x[i]]>
=0){//可达
//pNode的层数就是它在最优路径中的位置
inttemp_cc=pNode->
cc+City_Graph[pNode->
x[i]];
inttemp_rcost=pNode->
rcost-miniOut[pNode->
s]];
//下一个结点的剩余最小出边费用和
//等于当前结点的rcost减去当前这个结点的最小出边费用
if(temp_cc+temp_rcost<
Best_Cost){//下一个结点的最小出边费用和小于当前的最优解,说明可能存在更优解
for(j=0;
j++){//完全copy路径,以便下面修改
temp_x[j]=Best_Cost_Path[j];
temp_x[pNode->
s+1]]=Best_Cost_Path[i];
//将当前结点的编号放入路径的深度为s+1的地方
temp_x[i]=Best_Cost_Path[pNode->
s+1];
//将原路//径中的深度为s+1的结点编号放入当前路径的
//相当于将原路径中的的深度为i的结点与深度W为s+1的结点交换
Node*pNextNod
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 论文 分治 分支 限界