如何使用Python/Numpy在原点居中的八边形内均匀采样二维点
正多边形内均匀采样二维点(以八边形为例,可推广)
你的代码思路可行,但max_radii的计算有误,导致图形边缘出现弯曲。下面提供两种解决方案,第二种可直接推广至任意正多边形(如十六边形)。
方案一:修正极坐标法(针对八边形)
若要生成外接圆半径为1的正八边形(顶点到原点距离为1),正确的最大半径计算需先将角度映射到单个扇区,再计算相对于扇区对称轴的偏移,最终通过余弦关系推导半径。修正后的代码如下:
import numpy as np import matplotlib.pyplot as plt def sample_within_octagon(num_points): # 生成随机角度 angles = np.random.uniform(0, 2 * np.pi, size=num_points) # 正八边形单个扇区的角度:2π/8 = π/4 sector_angle = np.pi / 4 # 将角度映射到首个扇区[0, π/4),再计算相对于扇区对称轴的偏移 phi = angles % sector_angle - sector_angle / 2 # 计算对应角度的最大半径 max_radii = 1 / np.cos(phi) # 生成均匀分布的半径(用sqrt避免中心区域点过于密集) radii = np.sqrt(np.random.uniform(0, max_radii**2, size=num_points)) # 极坐标转笛卡尔坐标 x = radii * np.cos(angles) y = radii * np.sin(angles) return np.column_stack((x, y)) # 测试采样效果 num_points = 10000 points = sample_within_octagon(num_points) plt.scatter(points[:, 0], points[:, 1], s=1) plt.axis('equal') plt.show()
方案二:通用正多边形采样法(支持任意边数)
该方法可直接适配正十六边形、正三角形等任意正多边形,核心逻辑是在单个等腰三角形扇区内均匀采样,再旋转复制到所有扇区:
import numpy as np import matplotlib.pyplot as plt def sample_regular_polygon(num_points, n_sides, radius=1): # 单个扇区的中心角 sector_angle = 2 * np.pi / n_sides # 正多边形的内切圆半径(中心到边的距离) apothem = radius * np.cos(np.pi / n_sides) # 分配每个扇区的采样点数 points_per_sector = num_points // n_sides remaining = num_points % n_sides total_points = points_per_sector * n_sides + remaining points = np.zeros((total_points, 2)) # 生成单个扇区的采样点(利用对称性,先采一半再镜像) sample_count = points_per_sector + (1 if remaining > 0 else 0) # 沿对称轴方向采样x x = np.random.uniform(0, apothem, size=sample_count) # 垂直对称轴方向的y最大值由x决定,确保点在扇区内 y = np.random.uniform(0, x * np.tan(np.pi / n_sides)) # 生成对称点,组成完整扇区 sector_points = np.vstack((np.column_stack((x, y)), np.column_stack((x, -y)))) # 旋转复制到所有扇区 idx = 0 for k in range(n_sides): rot_angle = k * sector_angle # 旋转矩阵 rot_matrix = np.array([ [np.cos(rot_angle), -np.sin(rot_angle)], [np.sin(rot_angle), np.cos(rot_angle)] ]) # 旋转并写入结果数组 rotated_points = sector_points @ rot_matrix points[idx:idx+len(rotated_points)] = rotated_points idx += len(rotated_points) # 缩放至目标外接圆半径 points *= radius / apothem return points # 测试正八边形 num_points = 10000 octagon_points = sample_regular_polygon(num_points, n_sides=8, radius=1) plt.scatter(octagon_points[:, 0], octagon_points[:, 1], s=1) plt.axis('equal') plt.show() # 测试正十六边形 hexadecagon_points = sample_regular_polygon(num_points, n_sides=16, radius=1) plt.scatter(hexadecagon_points[:, 0], hexadecagon_points[:, 1], s=1) plt.axis('equal') plt.show()
方案优势:
- 完全通用,仅需修改
n_sides参数即可生成任意正多边形内的均匀点; - 采样逻辑直观,避免极坐标下的角度计算误差;
- 点分布均匀,不会出现边缘弯曲问题。
内容的提问来源于stack exchange,提问作者calmcc
相关产品推荐
相关产品推荐

