分枝定界法求哈密尔登回路问题的由表及里

刻苦努力的奋斗,终于有了学习成果,虽然写出的仅仅是非常拙略,功能单一的,简单的小程序代码,但不失为在代码学习的进步。总算是有了含有自己智慧在其中的结晶。其实这段代码能够顺利完成,虽然是独立完成,但终究还是含有侥幸和巧合的成分的。下面的内容将会使读者对这段代码有一个更为深入的了解。

在写出这段代码之前,我可以说是在黑白不分的日子中艰苦奋斗,捞着相关文章就是一通狂读,遇到不解的程序内容就是一顿狂练,功夫不负有心人,在这种近乎疯狂的学习之后,我在MATLAB的学习道路越走越远.……

在学习MATLAB的过程中我在MATLAB论坛中也搜寻着自己能看懂的文章和代码,以便巩固自己的所学,使得境界更进一步,这样才会使得自己对代码的运用更加灵活,不知不觉下载到了一篇有关算法的文章,内容都是很基础的算法,其中有计算速度和加速度的,包括了简单积分法,和简单公式法,而对我能够编写出最短回路程序帮助最大的例题是与计算路径问题相关的几道例题,虽然这几道例题的整体算法非常差劲,我之所以这么说是因为,他的整体编程思想是最简单的穷举法,凡是对程序编写有了解的人都明白何为穷举法,就是将所有可能的方法利用其中的规律全部列举出来,要知道穷举法应用到最短路算法中的话,这耗的不仅仅是时间而是CPU的速度,要是CPU运算速度非常快,那么有多少回路都会很快的列举出来;若是CPU运算速度非常慢那么即便是列举出非常少的路径,也会花费非常要命的时间。这样对于一个非常需要工作效率的工作的话可就恰到好处的扼住了命运的咽喉。许多成败在此一刻的胜负争夺便就此落下帷幕,很是令人不爽。

虽然这些例题本身都有错误,但是撇开他的错误却是有一点非常值得借鉴的地方,就是利用矩阵本身的定位方法作为代码运算的依据,这样既可以算出回路同时也能更方便的表示出回路的路径,真的不知道这个启发对我有着多么巨大的帮助,能够读到这篇文章是一件多么侥幸的事情啊,并且在错误中发现有价值的方法,更是难上加难的巧合,因此我要感谢这篇文档的编写者,不知道他对我有多么巨大的启发。

下面我们便由这些例题作为我的代码讲解的引述好了:

例题一:当推销员从I城出发,经过每个城市一次且仅一次,最后回到I城,问按怎样的路线走,使总的行程路线最短.四个城市间的距离如下表:

I

II

III

IV

I

0

8

5

6

II

6

0

8

5

III

7

9

0

5

IV

9

7

8

0

 

〔讲评〕

师:该问题可以用枚举法吗?

生:可以,从I出发有3种选择,第二步只有2种选择,最后一步回I城。

师:如何表示每一步的路长呢?

生:设计一个4×4路长矩阵a, a(i,j)表示城i到城j的路长。

解决这个问题的示例代码如下:

a=[0 8 5 6

6 0 8 5

7 9 0 5

9 7 8 0]; %a,路长矩阵

d=999;%设置总路长初值

   for i=2:4;

      d1=2:4;d1(d1==i)=[];%保证下一步i不被选

      for j=d1;

         d2=d1;d2(d2==j)=[]; %保证下一步j不被选,此时d2中只有一个数

         b=a(1,i)+a(i,j)+a(j,d2)+a(d2,1); %a,计算路长

            if b<d

               d=b; %保留更短的路径

               p=[1 i j d2 1];%p,记录路径

            end; end; end;

例题二:如图,线段旁边的数据表路长,箭头表路的方向,求节点 1 到节点9 的最短路.

〔讲评〕

师:如何用数据描述上图?

生:用9×9矩阵a,a(i,j)表节点i,j间的路长

师:a(4,1),a(3,1)等于多少?

生:无穷大。

师:a(1,1),a(2,2) 等于多少?

生:0

师:如何找最短路径?

生:枚举法,很简单!

师:9个点,要算8!=40320次,若20个点,要算19!>1017次,计算机要算瘫了。

通常采用Dijkstra算法,

第一步:设从点i到j的最短路长f(i,j),就是这点i到j的路长a(i,j)。f也是一个 9×9的矩阵。

 第二步,寻找“两边之和小于第三边”,即在 (k=1,2,9)中寻找使d(k)=a(i,k)+f(k,j) 最小的k,对应的d(k)的值,作为“改良”的 点i到j的最短路长f(i,j)

第三步,重复第二步的工作,考虑到最短路径最多8条边,所以,第二步的重复次数不会超过8

此题示例代码如下:

a=[ 0     6     3     1   Inf   Inf   Inf   Inf   Inf

   Inf     0   Inf   Inf     1   Inf   Inf   Inf   Inf

   Inf     2     0     2   Inf   Inf   Inf   Inf   Inf

   Inf   Inf   Inf     0   Inf    10   Inf   Inf   Inf

   Inf   Inf   Inf     6     0     4     3     6   10

   Inf   Inf   Inf   Inf     10     0     2   Inf   Inf

   Inf   Inf   Inf   Inf   Inf   Inf     0     4   Inf

   Inf   Inf   Inf   Inf   Inf   Inf   Inf     0   3

   Inf   Inf   Inf   Inf     inf   Inf   Inf     inf     0];

%a,路长矩阵

l=9; %节点数

f=a;%f最短路长矩阵;

n=1;

while n<8 %n,控制循环次数

   for i=1:l 

   for j=1:l

            for k=1:l

         d(k)=a(i,k)+f(k,j); %寻找“两边之和小于第三边”的点

      end 

      f(i,j)=min(d);

   end;end;

n=n+1;

end

%*********以下是寻找最短路径***

for i=1:l  

   for j=1:l;

      m=i;p{i,j}=i;%p{i,j}, p{i,j}:点i到点j的最短路径;

      while f(m,j)~=a(m,j)

      for k=1:l

         if (f(m,j)==a(m,k)+f(k,j))&(k~=m)&(k~=j)

            p{i,j}=[p{i,j} k];m=k;

              break

         end

         end

         end;

   p{i,j}=[p{i,j} j];

  end;end;

例题三:

下图为一网络,节点1到节点2的宽带带宽为6兆,节点1到节点3的宽带带宽为2兆,节点2到节点4的宽带带宽为3兆,…节点4到节点6的宽带带宽为2兆,求节点1到节点6的最大网速。

〔讲评]

师:这个问题用Lingo建模,非常简介,但是用Matlab也可以。

解决这种问题往往分两步:

第一步:寻找从节点1到节点6的通道,并算出该通道的最大网速,并计算出该通道中各宽带剩下的带宽容量,例:节点1,3,5,6通道,可获得网速2,节点1到3的容量变为0,节点3到5变为5,节点5到6变为5。

第二步:重复第一步的工作,直到找不到从节点1到节点6的通道为止。

生:如何寻找节点1到节点6的通道呢?

师:通道就是路径,我们可以给容量不为0的宽带定义路长1,只要我们找到从节点1到节点6的最短路长,如果不是无穷大,那么相对应的路径就是一条通道

此题示例代码如下:

a=[inf  1.3   2.1  0.9  0.7   1.8   2.0  1.5

1.3     inf  0.9   1.8  1.2   2.6   2.3  1.1

2.1    0.9    inf  2.6    1.7   2.5   1.9  1.0

0.9    1.8    2.6   inf    0.7   1.6   1.5  0.9

0.7   1.2   1.7    0.7    inf   0.9    1.1  0.8

1.8   2.6   2.5    1.6    0.9    inf  0.6  1.0

2.0   2.3   1.9    1.5    1.1    0.6   inf  0.5

1.5    1.1    1.0    0.9    0.8    1.0   0.5  inf];

%a:林区间道路长矩阵

l=length(a);

n=1;%n,计算道路条数

f=zeros(l);%f,记录选中的道路

u=1:l;%u:没连通的林区

[r c]=find(a==min(min(a))); %找最短的道

f(r(1),c(1))=1;%f,记录

u(u==r(1))=[];u(u==c(1))=[];

p=[r(1) c(1)];%p:已连通的林区

while n<l-1

   a(p,u)=a(p,u)+0.001;%此处加0.001是为了保证下一步找到最短道路所连的两个林区

   [r c]=find(a==min(min(a(p,u))));

 a(p,u)=a(p,u)-0.001; %还原0.001

f(r(1),c(1))=1;

%以下将c(1)记作已连通的林区

u(u==r(1))=[]; 

u(u==c(1))=[];

p=[p r(1) c(1)];   

   n=n+1;end

这三段代码给予人的启发是非常巨大的,最然第二题和第三题的代码需要整合这三道题的所有思路之后才能正确的编写出属于各自例题的代码,因此从三道例题整体上看并没有错误,但是若是一道例题一道例题分开着看的话,相信每一个做过这些例题的人都会知道只有第一道题的运算结果是部分正确的。

我在这里就不再赘述第二题和第三题的正确解法,这不是本文所讨论的关键所在,希望对此感兴趣的读者自己独立完成后两道题的正确求解过程,相信你会有精彩的发现。

之后呢我便根据这些代码所获得的经验总结出了我自己的代码思路,依据分枝定界法,编写出了自己能够独立完成的代码:


a=[0 20 1 11 2
    0 0 9 1 3
    0 0 0 13 8
    0 0 0 0 6
    0 0 0 0 0];

l=length(a)
s1=inf  %标记的非H回路的路程和
zp=inf  %标记的H回路的路程和
n2=1
f=a
f(a==0)=inf
b=zeros(l)
i1=0
while i1<=l-1
    [r c]=find(f==min(min(f)))
    b(r(1),c(1))=f(r(1),c(1))
    f(r(1),c(1))=inf
    i1=i1+1
end
f1=f
bz=b

%以上标定五个最小值
[rz cz]=find(b>0)
pathz=[rz cz]
pz=[rz;cz]
p2z=zeros(2*l,1)
i2z=1
n2z=0
    while i2z<=2*l
        [r2z c2z]=find(pz==pz(i2z,1))
        k1z=size(r2z)
        if k1z(1,1)>2
            p2z(r2z,1)=pz(r2z,1)
            n2z=n2z+1
        end
        i2z=i2z+1
    end
    %----------
if n2z<=2
    HHL=b
    zp=sum(sum(b))
else
    %------------------------
while min(min(f1))~=inf
    if n2>2
        b=snh
    end
%以下计算重复点
    [r1 c1]=find(b>0)
    path1=[r1 c1]
    p1=[r1;c1]
    p2=zeros(2*l,1)
    i2=1
    n2=0
    while i2<=2*l
        [r2 c2]=find(p1==p1(i2,1))
        k1=size(r2)
        if k1(1,1)>2
            p2(r2,1)=p1(r2,1)
            n2=n2+1
        end
        i2=i2+1
    end
    [r3 c3]=find(p2>0)
    p3=zeros(l,2)
    i3=0
    while i3<=n2-1
        if r3(1)<=l
            p3(r3(1),:)=path1(r3(1),:)
        else
            p3(r3(1)-l,:)=path1(r3(1)-l,:)
        end
        r3(1)=[]
        i3=i3+1
    end
    p3(p3==0)=[]
    p3=reshape(p3,n2,2)
    p8=p2
    [r8 c8]=find(p8>0)
    %以上计算重复点
    %以下为在其他矩阵中排除重复
   
    p9=p8
    r9=r8
    i4=1
    while i4<=n2
        f1(p9(r9(1),1),:)=inf
        f1(:,p9(r9(1),1))=inf
        r9(1)=[]
        i4=i4+1
    end

    %以上在其他矩阵中排除重复点
    %以下求出排除过相同点的矩阵中的最小值
    [r4 c4]=find(f1==min(min(f1)))
    f1(r4,c4)=inf
    b1=b
    b1(r4,c4)=a(r4,c4)
    %以上求出排除过相同点的矩阵中的最小值
    %以下开始循环替换并进行标记
    i5=1
    p4=p3
    while i5<=n2    %替换的次数基于重复的点数
        b1=b
        b1(r4(1),c4(1))=a(r4(1),c4(1))
        b1(p4(1,1),p4(1,2))=0
        p4(1,:)=[]
        %以下判断本次循环替换中的路径是否为H回路
        [r5 c5]=find(b1>0)
        p5=[r5;c5]
        i6=1
        n6=0
        while i6<=2*l
            [r6 c6]=find(p5==p5(i6,1))
            k6=size(r6)
            if k6(1,1)>2
                n6=n6+1
            end
            i6=i6+1
        end
        if n6>2
            if sum(sum(b1))<s1
                snh=[]
                s1=sum(sum(b1))
                snh=b1
            end
        else
            if sum(sum(b1))<zp
                HHL=[]
                zp=sum(sum(b1))
                HHL=b1
            end
        end
        i5=i5+1
    end
end
end
%以下为结果整理
[rs cs]=find(HHL>0)
minpaths=[rs cs]
journeys=zp

今天对这篇文章的编写算是简单的有了一个比较小的总结,相信明天的总结会更精彩。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值