图论模型
1.最短路程模型
2.TSP问题
3.最小生成树
最短路程模型
主要算法是Dijkstra算法(指定两点的最短线路,复杂度为o(n2))和Floyd算法(任意两点的最短路线,复杂度为o(n3))
给定网络N=(V,E,W) (顶点集合,边的集合,边的权值),求 i 点到 j 点的最短路线。
Dijkstra算法:
- 给出初始点集合P={ i },剩余点集合Q={1,2,3……n}-P,i点到各点直接距离Ur=wir(r=1,2,3…n)
- 在Q中寻找到i点距离最小的点k,使得Uk=min{ Ur},并置P∪{k} ➡P,Q-{k}➡Q (即把k点从集合Q转移到集合P)
- 对Q中每一个r,如果Uk+wkr<Ur,则Uk+wkr➡Ur,然后返回第二步直到找到 j 点为止
【此算法经过n-1次结束,在整个算法过程中,第二步最多要做(n-1)(n-2)/2次比较,因此计算量为o(n2)阶】
Floyd算法:
- 根据已知的部分节点之间的连续信息,建立初始距离矩阵B(i,j),其中把没有给出距离的赋予一个充分大的数值(这样就相当于“此路不通”,不会妨碍接下来的计算),以便于更新。(i,j=1,2,3…n)
- 进行迭代计算,对任意两点( i, j ),若存在k,使B(i,k)+B(k,j)<B(i,j),则更新B(i,j)=B(i,k)+B(k,j).
for k=1:n
for i=1:n %只要三个循环的Floyd就是屑
for j=1:n %然而我从未想到过,我更屑
t=B(i,k)+B(k,j)
if t<B(i,j) B(i,j)=t;end
end
end
end
在运行之前,要复制一个二维数组,表示每两点之间的距离
例如:
点1 | 点2 | 点3 | |
---|---|---|---|
点1 | 0 | 23 | 10000 |
点2 | 23 | 0 | 15 |
点3 | 10000 | 15 | 0 |
其中点1和点3没有直接连接,故用一个充分大值
n=总点数
A=zeros(n,n)
for i=1:n
for j=1:n
if(i==j) A(i,j)=0;
else A(i,j)=10000; %首先全部赋值一个充分大值
end
end
end
%然后一个个赋值(此处省略)
%接下来使矩阵对称
for j=1:n
for i=1:j-1
A(j,i)=A(i,j);
end
end
对于结果的输出:
fid=fopen('distance.txt','w');
for i=1:n
for j=1:n
fprintf(fid,'%4d',B(i,j));
end
fprintf(fid,'\n');
end
fclose(fid)
TSP问题
常用的方法有近邻法,贪心算法,最近插入法,模拟退火算法,遗传算法
设城市之间距离用矩阵d表示,dij表示城市 i 与城市 j 的距离(若没有,则用Floyd算)。同时设0-1矩阵X用来表示经过的各城市之间的路线。
考虑到到达每个城市之后能且只能去往另一个城市,则
同时会出现一种子圈问题:
以55的矩阵为例
为避免产生子圈应增加约束:
ui-uj+nxij≤n-1 (1<i≠j≤n)
在这里给出简单的解释:
如果 i 点,k点和 j 点形成了子圈,那么就会有
xij=1, xjk=1, xki=1
分别带入此式,则有
ui-uj≤-1,uj-uk≤-1,uk-ui≤-1
此三式相加,则会有0≤-3,矛盾
区区人类居然可以想出如此绝妙的方法
因此总的线性规划模型就是:
可采用lingo进行编程:
MODEL:
SETS:
city/1..10/:u; !以十个城市为例
link(city,city):d,x;
ENDSETS
DATA
d=(一张10*10的二维数组);
@text()=@writefor(link(i,j)|x(i,j)#GT#0:'x(',i,',',j,')=',x(i,j))
ENDDATA
MIN=@SUM(link:d*x);
@for(city(j):@sum(city(i)|j#ne#i:x(i,j))=1);
@for(city(i):@sum(city(j)|j#ne#i:x(i,j))=1);
@for(link(i,j)|i#ne#j#and#i#gt#1:u(i)-u(j)+10*x(i,j)<=9);
@for(link:@BIN(x));
End
【注意,如果问题并不要求要返回到出发点,我们可以考虑引入一个虚拟点n+1(类似画辅助线做几何题)。此点与各个地点距离都为0,这样可以使链和圈等价】(这真的是人类的脑子想出来的吗)
lingo编程规则
lingo编程用四部分组成
- 定义集合
sets: !以sets开头,endsets结尾,这两行不加引号
name/member_list/:attribute,etc; !定义一个原始集合
name(set1,set2,etc):attribute,etc; !定义一个导出集合
endsets
!例
Person/1..10/:A; !这是一个有十个元素的集合,名字是person,存在A中
Task/1..12/:B;
Link(Person,Task):x; !这是一个10*12二维数组,名字叫link
- 目标与约束
在此部分写目标函数和约束条件 - 数据部分
DATA:
attribute=value_list;
ENDDATA
- 初始化部分
对集合的属性(数组)定义初值。
INIT:
attribute=value_list;
ENDINIT
对内部函数的引用
以@打头
@ABS(x) | 绝对值 |
---|---|
@cos(x) | 余弦值 |
@EXP(x) | ex |
@FLOOR(x) | 向0靠近返回整数部分 |
@LGM(x) | 返回Γ函数自然数对数值 |
@LOG(x) | x的自然对数值 |
@SIGN(x) | 返回x的符号值,x<0,返回-1,x大于0,返回1 |
@SMAX(x1,x2…xn) | 返回最大值 |
集合函数的操作
@FOR(set_name:constraint_expressions) !对集合set_name的每个元素独立地生成约束
@MAX(set_name:expression) !返回集合上表达式的最大值
@SUM(set_name:expression:expression) !求和
@SIZE(set_name) !返回元素的个数
@IN(set_name,set_element) !如果集合set_name中包含元素set_element,返回1,否则返回0
逻辑语
#AND# | 与 |
---|---|
#OR# | 或 |
#NOT# | 非 |
#EQ# | = |
#NE# | ≠ |
#GT# | > |
#GE# | ≥ |
#LT# | < |
#LE# | ≤ |
变量界定
@BND(L,X,U) | L≤X≤U |
---|---|
@BIN(x) | 限制x为0或1 |
@FREE(x) | 取消对x的符号限制 |
@GIN(x) | 限制x为整数值 |
最小生成树
树
连通且不含圈的无向图,常用T表示。
树中的边称为枝,树中度为1(只有一只枝与其相接)的点为叶
若T是包含图G的全部顶点的子图,它又是树,则称T是G的生成树。
如果设T=(V,E)是赋权图G=(V,E)的一棵生成树,则称T中全部枝上的权数之和为生成树的权,记为w(T),即w(T)=Σw(e),如果T的权w(T)是G中所有生成树中最小者,则它为最小生成树,即w(T*)=Σmin{w(T)}
【赋权图:每条边上都有一个非负实数对应的图,此实数为权。例如道路图,每条路有一个对应的长度数值,此数值为权】
求解有
kruskal避圈算法和Prim破圈算法
在lingo中
设无向图有n个顶点,其赋权图的邻接矩阵为dn*n,dij表示边 i j的权,令dii=0。
现求根节点1到各节点生成的最小生成树。
model:
sets:
point/1..n/:u;
link(point,point):d,x;
endsets
data:
d=(n*n的表);
@text()=@writefor(link(i,j)|x(i,j)#GT#0:'x(',i,',',j,')=',x(i,j))
enddata
min=@sum(link(i,j)|i#ne#j:d(i,j)*x(i,j));
n=@size(piont);
@sum(point(j)|j#gt#1:x(1,j))>=1;
@for(point(i)|#ne#1:@sum(point(j)|j#ne#i:x(j,i))=1);
@for(link(i,j):@bin(x(i,j)));
@for(link(i,j)|i#ne#j:u(i)-u(j)+n*x(i,j)<=n-1);
end