别再死记硬背欧拉公式了!用Python可视化带你直观理解平面图的n-m+r=2
用Python可视化破解欧拉公式:平面图的n-m+r=2为何总成立?
第一次接触欧拉公式时,我盯着那个神奇的等式n-m+r=2看了很久——为什么结点数减去边数加上面数永远等于2?直到我用Python画出第一个平面图,看着代码实时计算这些数值,才真正理解了背后的几何直觉。今天我们就用可视化方法,让这个抽象公式变得触手可及。
1. 准备工作:理解平面图的基础概念
在开始编程前,我们需要明确几个关键术语。平面图(planar graph)是指可以画在平面上且边不相交的图。这里的"画"有严格定义:除了顶点处,任何两条边都不能有交叉。
平面图的核心要素:
- 结点(Node):图中的顶点,通常用圆圈表示
- 边(Edge):连接两个顶点的线段
- 面(Face):被边包围的区域,包括外部的无限区域
注意:计算面数时,不要忘记最外部的无限区域,它被称为"外部面"。
用NetworkX创建一个简单平面图试试看:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
G.add_edges_from([(1,2),(2,3),(3,4),(4,1),(1,3)])
pos = {1:(0,0), 2:(1,1), 3:(1,0), 4:(0,1)}
nx.draw(G, pos, with_labels=True)
plt.show()
这段代码绘制了一个四边形加一条对角线的图。你能数出它有4个结点、5条边和3个面吗?(包括外部面)
2. 验证欧拉公式的可视化工具开发
现在我们来构建一个交互式工具,它能自动计算并显示n-m+r的值。我们将使用Matplotlib的交互模式和NetworkX的组合。
2.1 基础可视化框架
from matplotlib.widgets import Button
class EulerVerifier:
def __init__(self):
self.fig, self.ax = plt.subplots(figsize=(8,6))
self.G = nx.Graph()
self.pos = {}
self.node_count = 0
self.edge_list = []
# 设置交互事件
self.fig.canvas.mpl_connect('button_press_event', self.on_click)
self.add_node_btn = Button(plt.axes([0.7, 0.05, 0.2, 0.075]), '添加结点')
self.add_node_btn.on_clicked(self.add_random_node)
def on_click(self, event):
if event.inaxes != self.ax: return
# 添加新结点或连接现有结点
...
def add_random_node(self, event):
# 在随机位置添加新结点
...
def update_display(self):
# 计算并显示当前图的n,m,r值
n = len(self.G.nodes())
m = len(self.G.edges())
r = 2 - n + m # 由欧拉公式推导
self.ax.set_title(f"n={n}, m={m}, r={r} | n-m+r={n-m+r}")
nx.draw(self.G, self.pos, ax=self.ax, with_labels=True)
plt.draw()
这个框架实现了:
- 点击画布添加结点
- 点击两个结点添加边
- 自动计算并显示欧拉公式结果
2.2 面数计算的实现技巧
面数r的计算有个巧妙之处——我们不需要实际识别每个面,利用欧拉公式反推即可。但为了直观展示,我们可以用平面图的平面嵌入特性:
def count_faces_visually(G, pos):
"""通过平面嵌入可视化计算面数"""
plt.clf()
nx.draw(G, pos, with_labels=True)
# 这里可以添加面着色的可视化代码
...
return len(identified_faces)
3. 经典平面图的实验验证
让我们用几个经典图例来验证欧拉公式。
3.1 完全图K4
K4是最简单的非平凡极大平面图,它有4个结点和6条边:
K4 = nx.complete_graph(4)
pos = nx.planar_layout(K4) # 自动生成平面嵌入
nx.draw(K4, pos, with_labels=True)
计算要素:
- 结点数 n = 4
- 边数 m = 6
- 面数 r = 4 (包括外部面) 验证:4 - 6 + 4 = 2 ✔
3.2 极大平面图特性
极大平面图是指添加任何一条新边都会破坏平面性的图。它们有个有趣性质:
每个面的度数都是3。用代码验证:
def check_maximal_planar(G):
if not nx.check_planarity(G)[0]: return False
embedding = nx.planar_layout(G)
faces = identify_faces(G, embedding)
return all(len(face) == 3 for face in faces)
3.3 K5-e:接近非平面图的边界
K5(完全5个结点的图)不是平面图,但去掉任意一条边(K5-e)就是平面图:
K5 = nx.complete_graph(5)
K5_e = K5.copy()
K5_e.remove_edge(0,1) # 任意删除一条边
pos = nx.planar_layout(K5_e)
nx.draw(K5_e, pos, with_labels=True)
计算验证:
- n=5, m=9 (完全图K5有10条边,减去1条)
- r=6
- 5 - 9 + 6 = 2 ✔
4. 从可视化到数学证明的桥梁
通过前面的实验,我们观察到了欧拉公式的正确性。现在让我们理解为什么它总是成立。
4.1 归纳法的可视化解释
欧拉公式可以用数学归纳法证明,而我们的可视化工具正好展示了这个过程:
- 基础情况:单个结点时,n=1,m=0,r=1 → 1-0+1=2
- 添加边:
- 添加新边连接已有结点:m增加1,r也增加1(分割面)→ n-(m+1)+(r+1)=n-m+r
- 添加新结点和边:n增加1,m增加1 → (n+1)-(m+1)+r=n-m+r
4.2 平面图的应用限制
欧拉公式只适用于连通平面图。用代码验证非连通图:
G = nx.Graph()
G.add_edges_from([(1,2),(2,3),(3,1),(4,5)]) # 两个连通分量
pos = {1:(0,0),2:(1,0),3:(0.5,1),4:(2,0),5:(3,0)}
nx.draw(G, pos, with_labels=True)
此时n=5,m=3,r=2 → 5-3+2=4≠2。对于k个连通分量,公式变为n-m+r=1+k。
5. 扩展应用:平面图判定的编程实现
欧拉公式引出了几个有用的平面图判定条件,我们可以将其实现为代码:
5.1 边数上限检查
对于简单连通平面图(n≥3),边数m≤3n-6:
def is_possible_planar(n, m):
if n < 3: return True
return m <= 3*n - 6
5.2 Kuratowski定理的简化检查
虽然完整实现Kuratowski定理的判定很复杂,但我们可以检查是否包含K5或K3,3的子图:
def contains_K5_subgraph(G):
if len(G.nodes()) < 5: return False
for nodes in itertools.combinations(G.nodes(), 5):
subgraph = G.subgraph(nodes)
if nx.density(subgraph) == 1: # 完全图
return True
return False
6. 交互式学习工具的高级功能
为了让学习体验更好,我们可以扩展我们的可视化工具:
6.1 实时面高亮
def on_hover(event):
if event.inaxes != self.ax: return
# 找到鼠标附近的面并高亮显示
...
6.2 自动生成平面图示例
def generate_sample_planar(self, event):
samples = {
'三角形': [(1,2),(2,3),(3,1)],
'四边形': [(1,2),(2,3),(3,4),(4,1)],
'K4': [(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)],
'K5-e': [(1,2),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5),(3,4),(3,5)]
}
...
6.3 保存和加载图状态
def save_graph(self, filename):
with open(filename, 'wb') as f:
pickle.dump({'G': self.G, 'pos': self.pos}, f)
def load_graph(self, filename):
with open(filename, 'rb') as f:
data = pickle.load(f)
self.G = data['G']
self.pos = data['pos']
self.update_display()
7. 从平面图到实际应用的思考
理解欧拉公式不仅是为了应付考试,它在许多实际场景中都有应用:
- 电路板设计:确保电路走线不交叉
- 地图着色:四色定理的基础
- 3D建模:多边形网格的拓扑分析
在实现地图着色算法时,我遇到过一个问题:为什么平面图的色数不超过4?正是通过这样的可视化实验,我才真正理解了背后的图论原理。
更多推荐


所有评论(0)