[工程师阿伟]正在和[机器小伟]一起研究[计算几何]]。
第一个部分:Graph
import xml.dom.minidom
import DataStruct as ds;
class Graph():
def readXmlFile(self, file):
xmldoc = xml.dom.minidom.parse(file)
Graph = xmldoc.getElementsByTagName("Graph")[0];
attr = Graph.attributes;
width = float(attr["width"].value);
height = float(attr["height"].value);
directed = attr["directed"].value;
weighted = attr["weighted"].value;
G = [width, height, directed, weighted];
print('图属性:', G);
Vertices = xmldoc.getElementsByTagName("Vertex");
V = [];
for vertex in Vertices:
attr = vertex.attributes;
vid = int(attr["vertexId"].value);
x = float(attr["x"].value);
y = float(attr["y"].value);
lable = int(attr["label"].value);
V.append([vid, x, y, lable]);
Edges = xmldoc.getElementsByTagName("Edge");
E = [];
for edge in Edges:
attr = edge.attributes;
tail = int(attr["tail"].value);
head = int(attr["head"].value);
E.append([tail, head]);
print('顶点集:', V);
print('边集:', E);
return (G, V, E);
def writeXmlFile(self, file, GVETuple):
file = open(file, "w")
file.write('\n')
G, V, E = GVETuple;
GAttr = ['width', 'height', 'directed', 'weighted'];
VAttr = ['vertexId', 'x', 'y', 'label'];
EAttr = ['tail', 'head'];
file.write('\n");
indent = ' '*2;
lev = 1;
file.write(lev*indent+'\n');
lev += 1;
for j in range(len(V)):
file.write(lev*indent+' \n");
lev -= 1;
file.write(lev*indent+' \n');
file.write(lev*indent+'\n');
lev+=1;
for j in range(len(V)):
file.write(lev*indent+' \n");
lev -= 1;
file.write(lev*indent+' \n');
lev -= 1;
file.write(' \n');
file.close()
#判断从起点到终点是否有通道
def graphDFS(self, graphTuple, start, goal):
# G = (V,E) is the graph with vertices, V, and edges, E.
G,V,E = graphTuple;
stack = ds.Stack()
visited = ds.HashSet()
stack.push(start)
path = [[start]];
while not stack.isEmpty():
# A vertex is popped from the stack. This is called the current vertex.
current = stack.pop()
# The current vertex is added to the visited set.
visited.add(current)
# If the current vertex is the goal vertex, then we discontinue the
# search reporting that we found the goal.
if current == goal:
#return True # or return path to goal perhaps
path[0] += [goal];
return path;
cursor = path[0];
path = path[1:];
# Otherwise, for every adjacent vertex, v, to the current vertex
# in the graph, v is pushed on the stack of vertices yet to search
# unless v is already in the visited set in which case the edge
# leading to v is ignored.
for v in self.adjacent(current, E):
if not v in visited:
stack.push(v)
if (v != goal):
path.append(cursor+[v]);
# If we get this far, then we did not find the goal.
#return False # or return an empty path
return [];
#在边集中含有当前点的边
def adjacent(self, current, E, directed = True):
#E为边集,格式为[[tail, head], ...]
V = [];
if (directed):
for i in range(len(E)):
if (E[i][0] == current):
V.append(E[i][1]);
else:
for i in range(len(E)):
if (E[i][0] == current):
V.append(E[i][1]);
elif (E[i][1] == current):
V.append(E[i][0]);
return V;
class __Vertex:
def __init__(self,vertexId,x,y,label):
self.vertexId = vertexId
self.x = x
self.y = y
self.label = label
self.adjacent = []
self.previous = None
class __Edge:
def __init__(self,v1,v2,weight=0):
self.v1 = v1
self.v2 = v2
self.weight = weight
def __lt__(self,other):
return self.weight < other.weight
def tmp():
graph = Graph();
data = graph.readXmlFile("graph.xml");
#graph.writeXmlFile("graphcopy.xml", data);
#由于用例的顶点序号和它的标签数字不一样,所以有了下面这么多变换
path = graph.graphDFS(data, 10, 9);
print('path: ', path);
G,V,E = data;
vNum = len(V);
for k in range(len(path)):
print('第{0}条路径'.format(k+1));
for i in range(len(path[k])):
print(V[vNum-path[k][i]-1][3],'->', end = '');
print('');
一张不考虑权重的有向图:
#图1
图属性: [595.8, 229.2, 'True', 'False']
顶点集: [[12, 343.15, 156.1, 10], [11, 246.15, 161.1, 9], [10, 288.15, 58.1, 0], [9, 374.15, 58.1, 1], [8, 135.15, 156.1, 6], [7, 49.65, 83.1, 2], [6, 167.15, 83.05, 3], [5, 121.15, 19.1, 8], [4, 419.15, 204.1, 11], [3, 426.15, 87.1, 4], [2, 546.15, 96.1, 5], [1, 546.15, 210.1, 7], [0, 485.15, 161.1, 12]]
边集: [[12, 10], [10, 6], [6, 11], [7, 6], [7, 8], [7, 5], [11, 12], [12, 4], [4, 0], [0, 2], [2, 1], [2, 3], [3, 9], [9, 10], [9, 12]]
if (1) {
var text = new DrawText();
plot.translate(0, 200);
//有向图两点间画向量连接,否则用直线连接
var drawVector = $Graph[2];
var vNum = $Verts.length;
var eNum = $Edges.length;
var x1, y1, x2, y2, dx, dy;
var P1, P2;
for (var i = 0; i < eNum; i++) {
P1 = $Verts[vNum-$Edges[i][0]-1];
P2 = $Verts[vNum-$Edges[i][1]-1];
x1 = P1[1];
y1 = P1[2];
x2 = P2[1];
y2 = P2[2];
dx = (x2-x1)/4;
dy = (y2-y1)/4;
x1 += dx;
x2 -= dx;
y1 += dy;
y2 -= dy;
if (drawVector == "True") {
shape.vectorDraw([[x1, -y1], [x2, -y2]], 'blue', 1);
}
else {
shape.multiLineDraw([[x1, -y1], [x2, -y2]], 'blue', 1);
}
}
for (var i = 0; i < vNum; i++) {
text.textWithCircle([$Verts[i][3].toFixed(0)], $Verts[i][1], $Verts[i][2], 0,
'green', 30, 'M');
}
//文本显示方便类
function DrawText() {
this.protype = function(str, xPos, yPos, traits, rotate, style, fontSize, alignment) {
//traits参数指定了显示正常/加粗/斜体等区别
//style 是颜色/渐变的区别
//fontSize是字体字号的区别
//alignment是对齐方式的区别
rotate = rotate ? rotate : 0;
style = style ? style : 'black';
fontSize = fontSize >= 10 ? fontSize : 20;
//左对齐0, 中对齐1, 右对齐2
alignment = alignment ? alignment : 'L';
var tmp = fontSize.toFixed(0)+'px';
var font = '';
if (traits == 'normal') {
font = "normal normal normal "+tmp+" Times New Roman";
}
else if (traits == 'bold') {
font = 'normal normal 800 '+tmp+' Arial';
}
else if (traits == 'italic') {
font = 'italic normal bold '+tmp+' Microsoft Sans Serif';
}
else {
font = "normal normal normal "+tmp+" Times New Roman";
}
plot.save()
.setFont(font)
//.setTextBaseline('top')
.setFillStyle(style)
.translate(xPos, yPos)
.rotate(-rotate);
var x = 0, y = 0;
var measure = 0;
var s = '';
//多行
var len = str.length;
for (var i = 0; i < len; i++) {
s = str[i];
measure = plot.measureText(s);
if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){
//[x,y]为居中对齐的中点
plot.fillText(s, x-measure/2, y, measure);
}
else if (alignment == 2 || alignment.toUpperCase() == 'R'){
//[x,y]为右对齐的右边边界点
plot.fillText(s, x-measure, y, measure);
}
else {
//于[x,y]处左对齐
plot.fillText(s, x, y, measure);
}
y += fontSize*1.5;
}
plot.restore();
}
this.normal = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
return this.protype(str, xPos, yPos, 'normal', rotate, style, fontSize, alignment);
}
this.bold = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
return this.protype(str, xPos, yPos, 'bold', rotate, style, fontSize, alignment);
}
this.italic = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
return this.protype(str, xPos, yPos, 'italic', rotate, style, fontSize, alignment);
}
//计算文字显示需要的边界范围,这是一个矩形,并且考虑到了文字的旋转和多行问题
this.calcRectBound = function(str, xPos, yPos, rotate, fontSize, alignment) {
rotate = rotate ? rotate : 0;
fontSize = fontSize >= 10 ? fontSize : 20;
//左对齐0, 中对齐1, 右对齐2
alignment = alignment ? alignment : 'L';
var font = "normal normal normal "+fontSize.toFixed(0)+'px'+" Times New Roman";
plot.save()
.setFont(font);
var lines = str.length;
var maxChars = 0;
var lineChars = 0;
for (var i = 0; i < lines; i++) {
lineChars = plot.measureText(str[i])+5;
if (lineChars > maxChars) {
maxChars = lineChars;
}
}
var boundWidth = maxChars;
var boundHeight = fontSize*1.5*lines;
var hMargin = 0;
var array = [];
//根据行数确定偏移距离
if (lines == 1) {
hMargin = -boundHeight/2+Math.floor(fontSize/3);
}
else {
hMargin = fontSize/2 - 1.5*(lines-1)*fontSize/2;
}
if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){
array = [[boundWidth/2, 0],
[-boundWidth/2, 0],
[-boundWidth/2, boundHeight],
[boundWidth/2, boundHeight]];
}
else if (alignment == 2 || alignment.toUpperCase() == 'R'){
array = [[0, 0],
[-boundWidth, 0],
[-boundWidth, boundHeight],
[0, boundHeight]];
}
else {
array = [[boundWidth, 0],
[0, 0],
[0, boundHeight],
[boundWidth, boundHeight]];
}
var transform = new Transform();
array = transform.translate(transform.rotate(transform.translate(array, 0, hMargin), rotate), xPos, -yPos);
plot.restore();
return array;
}
//计算文字显示需要的边界范围,这是一个椭圆形,并且考虑到了文字的旋转和多行问题
this.calcEllipBound = function(str, xPos, yPos, rotate, fontSize, alignment) {
rotate = rotate ? rotate : 0;
fontSize = fontSize >= 10 ? fontSize : 20;
//左对齐0, 中对齐1, 右对齐2
alignment = alignment ? alignment : 'L';
var font = "normal normal normal "+fontSize.toFixed(0)+'px'+" Times New Roman";
plot.save()
.setFont(font);
var lines = str.length;
var maxChars = 0;
var lineChars = 0;
for (var i = 0; i < lines; i++) {
lineChars = plot.measureText(str[i]);
if (lineChars > maxChars) {
maxChars = lineChars;
}
}
var boundWidth = maxChars;
var boundHeight = fontSize*1.5*lines;
var hMargin = 0;
var array = [];
var transform = new Transform();
//椭圆的长短半轴
var ea = boundWidth/2*1.2, eb = boundHeight/2*1.2;
//根据行数确定偏移距离
if (lines == 1) {
hMargin = fontSize/2;
}
else {
hMargin = fontSize/2 - 1.5*(lines-1)*fontSize/2;
}
if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){
array = shape.ellipse(ea, eb);
array = transform.translate(transform.rotate(transform.translate(array, 0, hMargin), rotate), xPos, -yPos);
}
else if (alignment == 2 || alignment.toUpperCase() == 'R'){
array = shape.ellipse(ea, eb);
array = transform.translate(transform.rotate(transform.translate(array, -boundWidth/2, hMargin), rotate), xPos, -yPos);
}
else {
array = shape.ellipse(ea, eb);
array = transform.translate(transform.rotate(transform.translate(array, boundWidth/2, hMargin), rotate), xPos, -yPos);
}
plot.restore();
return array;
}
this.textWithRect = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
var boundArray = this.calcRectBound(str, xPos, yPos, rotate, fontSize, alignment);
//hint(boundArray);
shape.strokeDraw(boundArray, style, 1);
this.normal(str, xPos, yPos, rotate, style, fontSize, alignment);
}
//文字伴随一柄小旗
//当旋转时,现在还未能完全算清楚准确的坐标变换
this.textWithFlag = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
var transform = new Transform();
shape.fillDraw(shape.nEdge(xPos, -yPos-5, 10, 3, Math.PI), style, 1);
var boundArray = this.calcRectBound(str, 0, 0, 0, fontSize, 'L');
//取阵列中间两个点,是为了加一柄棋杆。
var p_1x = boundArray[1][0], p_1y = boundArray[1][1],
p_2x = boundArray[2][0], p_2y = boundArray[2][1];
var dx = 1*(p_2x-p_1x), dy = 1*(p_2y-p_1y);
p_2x = p_2x-2*dx;
p_2y = p_2y-2*dy;
var line = [[p_1x, p_1y], [p_2x, p_2y]];
xPos = xPos - dx;
yPos = yPos - dy-fontSize/4;
boundArray = transform.translate(transform.rotate(boundArray, rotate), xPos, -yPos);
line = transform.translate(transform.rotate(line, rotate), xPos, -yPos);
shape.multiLineDraw([].concat(line), style, 1);
//hint(boundArray);
shape.strokeDraw(boundArray, style, 1);
this.bold(str, xPos, yPos, rotate, style, fontSize, 'L');
}
this.textWithEllipse = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
var boundArray = this.calcEllipBound(str, xPos, yPos, rotate, fontSize, alignment);
//hint(boundArray);
shape.strokeDraw(boundArray, style, 1);
this.normal(str, xPos, yPos, rotate, style, fontSize, alignment);
}
this.textWithCircle = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
var r = 30;
//hint(boundArray);
shape.strokeCircle(xPos, yPos, r);
this.normal(str, xPos, yPos+fontSize/4, rotate, style, fontSize, alignment);
}
//两个点连直线,上面写文字
this.textWithLine = function(str, xPos1, yPos1, xPos2, yPos2, style, fontSize, alignment) {
var rotate = xPos2 == xPos1 ? 0 : Math.atan((yPos2-yPos1)/(xPos2-xPos1));
//hint(boundArray);
shape.multiLineDraw([[xPos1, yPos1], [xPos2, yPos2]], style, 1);
this.bold(str, xPos, yPos, rotate, style, fontSize, alignment);
}
//两个点连向量,上面写文字
this.textWithVector = function(str, xPos1, yPos1, xPos2, yPos2, style, fontSize, alignment) {
var rotate = xPos2 == xPos1 ? 0 : Math.atan((yPos2-yPos1)/(xPos2-xPos1));
//hint(boundArray);
shape.vectorDraw([[xPos1, yPos1], [xPos2, yPos2]], style, 1);
this.bold(str, xPos, yPos, rotate, style, fontSize, alignment);
}
this.textWithSphere = function(str, xPos, yPos, rotate, style, fontSize, alignment, sphereR) {
var R = sphereR > 5 ? sphereR : 5;
shape.sphere([xPos, yPos], R, style);
this.bold(str, xPos, yPos-R, rotate, style, fontSize, alignment);
}
//一组圆圈,以第一圈为中心,在它的外边围一圈较小的圆
this.textWithCluster = function(str, xPos, yPos, rotate, style, fontSize, alignment) {
var centerD = fontSize*4;
var peripheralD = fontSize*3;
//中央大圆
var circle_C = shape.nEdge(xPos, yPos, centerD/2, 36, 0);
//传入的str应该是一个字符串数组,['s1', 's2', ...]这种格式
//把s1填入中央圆圈,其它的填入它周围的一系列圆圈中
//字符串数组中除第一个串以外的字符串的个数
var count = str.length - 1;
//小弟不能太少,到少留三个位置
if (count < 3) count = 3;
//内外圆的圆心距离 periOff > (centerD+peripheralD)/2
//同时要满足 (periOff* 6)/count > peripheralD
var periOff = Math.max((centerD+peripheralD)/2, peripheralD*count/6);
var periArray = shape.nEdge(0, 0, periOff, count);
var transform = new Transform;
periArray = transform.translate(transform.rotate(periArray, rotate), xPos, yPos);
//画圆圈集群
shape.strokeDraw([].concat(circle_C), style, 1);
var circle_P = [];
for (var i = 0; i < count; i++) {
circle_P = shape.nEdge(periArray[i][0], periArray[i][1], peripheralD/2, 36, 0);
shape.strokeDraw(circle_P, style, 1);
}
this.bold([str[0]], xPos, Math.abs(yPos)+0.5*fontSize, 0, style, fontSize, alignment);
for (var i = 0; i < count; i++) {
this.normal([str[1+i]], periArray[i][0], Math.abs(periArray[i][1])+0.4*fontSize, 0, style, fontSize*0.7, alignment);
}
}
}
###
# @usage 求两个给定地点,所有路径中的最短值
# @author mw
# @date 2016年01月13日 星期三 09:50:23
# @param
# @return
#
###
class DijkstraPath():
#初始化
def __init__(self, node_map):
self.node_map = node_map;
self.node_length = len(node_map);
self.used_node_list = [];
self.collected_node_dict = {};
#调用函数
def __call__(self, from_node, to_node):
self.from_node = from_node;
self.to_node = to_node;
self._init_dijkstra();
return self._format_path();
def _init_dijkstra(self):
self.used_node_list.append(self.from_node);
self.collected_node_dict[self.from_node] = [0, -1];
for index1, node1 in enumerate(self.node_map[self.from_node]):
if node1:
self.collected_node_dict[index1] = [node1, self.from_node];
self._foreach_dijkstra();
def _foreach_dijkstra(self):
#保证每个点最多只到一次
if len(self.used_node_list) == self.node_length - 1:
return;
#由于items()方法会改变原字典内容,所以此处拷贝副本
collected_node_dict = dict(self.collected_node_dict);
# 遍历已有权值节点
for key, val in collected_node_dict.items():
if key not in self.used_node_list and key != to_node:
self.used_node_list.append(key);
else:
continue;
# 对节点进行遍历
for index1, node1 in enumerate(self.node_map[key]):
# 如果节点在权值节点中并且权值大于新权值
if node1 and index1 in self.collected_node_dict \
and self.collected_node_dict[index1][0] > node1 + val[0]:
# 更新权值
self.collected_node_dict[index1][0] = node1 + val[0]
self.collected_node_dict[index1][1] = key;
elif node1 and index1 not in self.collected_node_dict:
self.collected_node_dict[index1] = [node1 + val[0], key];
#递归
self._foreach_dijkstra();
def _format_path(self):
node_list = [];
temp_node = self.to_node;
node_list.append((temp_node, self.collected_node_dict[temp_node][0]));
while self.collected_node_dict[temp_node][1] != -1:
temp_node = self.collected_node_dict[temp_node][1];
node_list.append((temp_node, self.collected_node_dict[temp_node][0]));
node_list.reverse();
return node_list;
def pathString(self, node):
node_list = self._format_path();
size = len(node_list);
s = '选择的路线是:\n';
for i in range(size):
if i < size-1:
s += str(node[node_list[i][0]])+'-->';
else:
s += str(node[node_list[i][0]]);
s+= ' ,这条路线总长为{0}米'.format(node_list[size-1][1]);
return s;
def set_node_map(node_map, node, node_list):
for x, y, val in node_list:
node_map[node.index(x)][node.index(y)] = node_map[node.index(y)][node.index(x)] = val
if __name__ == "__main__":
#节点表
node = [1, 2, 3, 4, 5, 6];
addressString = ['学校', '医院', '体育场', '公园', '小年宫', '小伟家'];
#带权重的边表
node_list = [(1,6,250), (1,2,300), (5,6,300), (4,5,470),
(3,4,150), (2,3,450),(2,5,250)];
#节点数量的平方
node_map = [[0 for val in range(len(node))] for val in range(len(node))];
#预设节点表
set_node_map(node_map, node, node_list);
#Demo 求A --> D 的最短距离
from_node = node.index(6);
to_node = node.index(3);
#求取路径
dijkstrapath = DijkstraPath(node_map);
#路径字符串
path = dijkstrapath(from_node, to_node);
print(dijkstrapath.pathString(addressString));