使用Python的Plotly等库绘制3D Permutohedron(排列多面体)的问题求助
我正尝试使用Python的plotly、numpy和pandas库绘制Permutohedron(排列多面体)。
这是我目前编写的代码:
import plotly.express as px import numpy as np import itertools import pandas as pd order = 4 items = range(1, order+1) permuted_items = np.array([*itertools.permutations(items)]) def closest_nodes(node, nodes): # 返回nodes中与node距离最近的实例 nodes = np.asarray(nodes) dist_2 = np.sum((nodes - node)**2, axis=1)**.5 indices = np.where(dist_2 == dist_2.min())[0] return nodes[indices] xyzs = [] colors = [] for i, point in enumerate(permuted_items[:-1]): closest_points = closest_nodes(point, permuted_items[i+1:]) for c_point in closest_points: xyzs.extend([point[:3], c_point[:3]]) # 生成唯一字符串作为颜色标识,用于绘图时对边进行分组 c = str(point) + str(c_point[:3]) colors.extend([c, c]) lines = np.array(xyzs) x, y, z = lines.T plotting_data = pd.DataFrame({ "X": x, "Y": y, "Z": z, "color": colors }) fig = px.line_3d(plotting_data, x='X', y='Y', z='Z', color="color") fig.show()
但输出的图形严重变形:
也就是我通过删除最后一维将形状投影到3D的方法改变了每条边的长度,导致所有边的长度都不等于√2。
我期望得到的正确形状如下:
请问应该如何修改代码才能得到正确的绘制结果?
问题原因
4阶排列多面体的所有顶点都位于4维空间的超平面x₁+x₂+x₃+x₄ = 10上,直接丢弃最后一维的投影方式不是等距投影,会破坏原有顶点间的距离关系,导致图形变形。
解决方案
你需要先将4维顶点等距投影到3维空间,再进行绘制。核心是先对4维顶点做中心化,再投影到超平面x₁+x₂+x₃+x₄=0的正交单位基上,即可保留原始距离关系。
修改后完整代码
import plotly.express as px import numpy as np import itertools import pandas as pd order = 4 items = range(1, order+1) permuted_items = np.array([*itertools.permutations(items)]) # 构造4维到3维的等距投影矩阵 # 超平面x1+x2+x3+x4=0的正交基 basis = np.array([ [1, -1, 0, 0], [1, 1, -2, 0], [1, 1, 1, -3] ]) # 基向量单位化 basis = basis / np.linalg.norm(basis, axis=1, keepdims=True) # 顶点中心化(去掉均值,平移到超平面x1+x2+x3+x4=0上) centered_points = permuted_items - permuted_items.mean(axis=1, keepdims=True) # 投影得到3维坐标 projected_3d = centered_points @ basis.T def closest_nodes(node, nodes): nodes = np.asarray(nodes) dist_2 = np.sum((nodes - node)**2, axis=1)**.5 indices = np.where(dist_2 == dist_2.min())[0] return nodes[indices] xyzs = [] colors = [] for i, point in enumerate(permuted_items[:-1]): closest_points = closest_nodes(point, permuted_items[i+1:]) for c_point in closest_points: # 使用投影后的3维坐标,而非原始前3维 c_idx = np.where((permuted_items == c_point).all(axis=1))[0][0] xyzs.extend([projected_3d[i], projected_3d[c_idx]]) c = str(point) + str(c_point) colors.extend([c, c]) lines = np.array(xyzs) x, y, z = lines.T plotting_data = pd.DataFrame({ "X": x, "Y": y, "Z": z, "color": colors }) fig = px.line_3d(plotting_data, x='X', y='Y', z='Z', color="color") # 设置等轴比例避免plotly自动拉伸 fig.update_layout(scene=dict(aspectmode='data')) fig.show()
运行后即可得到边长一致、形状正确的3D排列多面体。
内容的提问来源于stack exchange,提问作者Zoom
相关产品推荐
相关产品推荐

