算法准备
旅行商问题(TSP)是一个经典的图论问题。在给定一系列城市和他们之间的距离以后,一个旅行商人希望能够找到一条能够走遍所有城市,并返回起点城市的最短路径。既然路径能串起来所有的城市,那么问题中的一个隐藏条件是,每个城市都必须至少有另一个城市与它相连,否则就会存在某个点无论如何不会进入路径的情况。在相连的两个城市之间,应该有一个正数的距离或者旅行时间,不相连的城市之间则存在一个极大的正数。
为什么要强调这是一个图论问题呢?因为解决一个图论问题的前提是,你至少得有一张图。通常,一张图(Graph)是用节点(Node)和点之间的边(Edge)组成。在数学表达中,也经常使用V来表示节点的集合,取Vertex 之意。这也就意味着,我们需要先创建“点”和“边”两个类。在Eclipse中,先创建一个工程(Project),按自己的喜好命名。这里我命名为ACOForTSPDorigo1996。
2. 在工程中新建一个包(package),命名为“graph",包的命名通常使用小写。有些程序员也可能直接创建一个叫Graph的类,也是完全没有问题的,此次不展开讲解。
3. 在graph包中,新建一个类Node,通常类名的首字母大写。此处不需要对Node进行很复杂的定义,只需要给Node一个id,以示区分不同的节点就可以了。所以在一个Node中建一个private的String变量,命名为"nodeId"。通常变量的首字母为小写,且考虑到面向对象程序设计的封装特性,这个变量最好是private类型的,为了获取这个变量的值和给这个变量赋值,需要设getter和setter函数。代码如下:
public class Node {
private String nodeId;
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
}
同时,每个类应该有构造函数,构造函数必须为public类型。这里我写了两种构造方式。代码如下:
public Node() {
}
public Node(String nodeId) {
this.nodeId = nodeId;
}
最后,为了以后使用方便,再写了一个toString()函数,便于打印节点的内容,和复写了equals(Object)函数,便于节点之间的比较。因为这些函数都要被其他类调用,它们都是public类型。
public String toString() {
return "node: "+nodeId;
}
@Override
public boolean equals(Object o) {
Node node = (Node)o;
return this.nodeId.equals(node.nodeId);
}
至此,一个Node类便完成了。全部代码如下:
package graph;
public class Node {
private String nodeId;
public Node() {
}
public Node(String nodeId) {
this.nodeId = nodeId;
}
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String toString() {
return "node: "+nodeId;
}
@Override
public boolean equals(Object o) {
Node node = (Node)o;
return this.nodeId.equals(node.nodeId);
}
}
4. 在graph包中,新建一个类Edge。一条边应该有一个起点和一个终点,同时边具有长度/旅行时间,所以一条边应该至少包含三个变量:
public class Edge {
private Node startNode;
private Node endNode;
private int travelTime;
}
同时,构造函数也需要写一下:
public Edge() {}
按照需要也可以写toString()函数,但是本例中就不再使用了。至此,整个Edge类的全部代码如下:
package graph;
public class Edge {
private Node startNode;
private Node endNode;
private int travelTime;
public Edge() {}
public Node getStartNode() {
return startNode;
}
public void setStartNode(Node startNode) {
this.startNode = startNode;
}
public Node getEndNode() {
return endNode;
}
public void setEndNode(Node endNode) {
this.endNode = endNode;
}
public int getTravelTime() {
return travelTime;
}
public void setTravelTime(int travelTime) {
this.travelTime = travelTime;
}
}
到这里为止,TSP问题的准备工作就完成了。
如果这个TSP在将来需要拓展,还可以向这两个类中添加更多的变量。比如一个节点可能包含坐标点信息,可能包含地名信息,一条边同时包含长度和旅行时间信息,也可能包含时间信息,因为不同时间段中的旅行时间是可能变化的(早晚高峰与平时存在差异)。这一类的扩展,与具体的问题要求有关,在高阶的应用中再予以考虑。