哈密顿路径(Hamiltonian Path)的python实现

引言

哈密顿路径(Hamiltonian Path)是图论中的一个概念,它指的是一条通过图中每个顶点一次且仅一次的路径。具体而言,对于一个图 G,如果存在一条最短路路径,使得路径上的每个顶点都不重复,且路径经过图中的每个顶点一次,那么这条路径就是图 G 的哈密顿路径。
一个哈密顿路径是图的遍历,它访问每个节点一次,且不重复,形成一条路径。特别地,如果哈密顿路径的起点和终点相同,形成一个回路,那么这就是哈密顿回路。
哈密顿路径和哈密顿回路在图论中是重要的问题,与著名的旅行商问题(Traveling Salesman Problem)相关。在旅行商问题中,目标是找到一条经过所有城市一次且最短的路径。哈密顿路径的存在性和寻找算法对于解决旅行商问题提供了一些思路。
文章定义了一个名为Hamilton的类,结合了networkxgeopandasmatplotlib这几个 Python 库,通过这些库实现了哈密顿路径的生成并可视化。

构建图

我们自定义几个点存入gdf,然后根据gdf构建图(G),图中的节点信息来自gdf的id,而边的权重则根据点之间的距离确定。

import geopandas as gpd
import networkx as nx
import matplotlib.pyplot as plt
from shapely.geometry import Point

# 创建一个示例的geodataframe
data = {'id': ['P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
        'geometry': [Point(3, 2), Point(5, 3), Point(0, 1), Point(5, 7), Point(1, 3), Point(2, 1)]}

gdf = gpd.GeoDataFrame(data, crs='EPSG:4326')

# 构建无向图
G = nx.Graph()

# 添加点到图中
for index, row in gdf.iterrows():
    G.add_node(index, pos=(row['geometry'].x, row['geometry'].y))

# 添加边到图中(在这个示例中,任意两点之间都有一条边)
for i in range(len(gdf)):
    for j in range(i + 1, len(gdf)):
        distance = round(gdf.geometry.iat[i].distance(gdf.geometry.iat[j]),2)
        G.add_edge(i, j, weight=distance)

# 绘制图
pos = nx.get_node_attributes(G, 'pos')
labels = {index: str(gdf['id'].iloc[index]) for index in range(len(gdf))}

nx.draw(G,
        pos, 
        labels=labels,
        with_labels=True, 
        font_weight='bold', 
        node_size=200, 
        node_color='lightblue', 
        font_size=10, 
        font_color='black', 
        edge_color='gray', 
        linewidths=1, 
        alpha=0.7)

# 添加边的权重标签
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()

哈密顿路径(Hamiltonian Path)的python实现_第1张图片

定义Hamilton类

class Hamilton:
    def __init__(self, gdf):
        self.gdf = gdf
        self.G = None
        self.hamilton_nodes = None

    def generating_graph(self):
        self.G = nx.Graph()
        # 添加点到图中
        for index, row in self.gdf.iterrows():
            self.G.add_node(index, pos=(row['geometry'].x, row['geometry'].y))
        # 添加边到图中(在这个示例中,任意两点之间都有一条边)
        for i in range(len(self.gdf)):
            for j in range(i + 1, len(self.gdf)):
                distance = round(self.gdf.geometry.iat[i].distance(self.gdf.geometry.iat[j]), 2)
                self.G.add_edge(i, j, weight=distance)
        return self.G
        
    def points_order(self):
        self.generating_graph()
        # 根据边确定最长的两个点为起始点
        max_edge = max(self.G.edges(data=True), key=lambda x: x[2]['weight'])  
        start_node, end_node, weight = max_edge
        self.hamilton_nodes = [start_node,end_node]
        # 循环根据新点的插入系列获取最短路径,直到所有点遍历结束
        while len(self.hamilton_nodes)<len(self.G.nodes):
            other_node = [node for node in self.G.nodes if node not in self.hamilton_nodes][0]
            all_permutations = self.generating_permutation(self.hamilton_nodes, other_node)
            self.hamilton_nodes = min(all_permutations, key=self.calculate_distance)
        return self.hamilton_nodes

    def generating_permutation(self, lst, element):
        result = []
        # 遍历列表的每个位置(索引)
        for index in range(len(lst) + 1):
            # 创建一个新的列表,在指定位置插入元素
            new_lst = lst.copy()
            new_lst.insert(index, element)
            result.append(new_lst)  # 将新列表添加到结果中
        return result

    def calculate_distance(self, nodes):
        total_distance = 0
        for i in range(len(nodes) - 1):
            distance = self.G.get_edge_data(nodes[i], nodes[i+1])['weight']
            total_distance += distance
        return total_distance

    def plot_hamilton_path(self):
        self.generating_graph()
        self.points_order()
        pos = nx.get_node_attributes(self.G, 'pos')
        labels = {index: str(self.gdf[str(self.ID)].iloc[index]) for index in self.hamilton_nodes}
        nx.draw_networkx_nodes(self.G, pos, node_size=200, node_color='lightblue')
        nx.draw_networkx_labels(self.G, pos, labels=labels, font_size=8, font_weight='bold', font_color='black')
        hamilton_edges = [(self.hamilton_nodes[i], self.hamilton_nodes[i+1]) for i in range(len(self.hamilton_nodes)-1)]
        nx.draw_networkx_edges(self.G, pos, edgelist=hamilton_edges, edge_color='red', width=2)
        plt.show()
# 调用类
hamilton_instance = Hamilton(gdf)
graph = hamilton_instance.generating_graph()
ordered_nodes = hamilton_instance.points_order()
hamilton_instance.plot_hamilton_path()
print("Hamiltonian Nodes:", ordered_nodes)
print("Hamiltonian IDs:", gdf.loc[ordered_nodes]['id'].to_list())

哈密顿路径(Hamiltonian Path)的python实现_第2张图片

Hamiltonian Nodes: [2, 4, 5, 0, 1, 3]
Hamiltonian IDs: ['P3', 'P5', 'P6', 'P1', 'P2', 'P4']

你可能感兴趣的:(geopandas,python)