我正在尝试设计一种生成随机2D凸多边形的方法。它必须具有以下属性:
例如,生成具有10个顶点并位于square [0..100] x [0..100]内的随机多边形。
使这项任务变得困难的原因是坐标应该是整数。
我尝试的方法是在给定方格中生成随机点集并计算这些点的凸包。但是与N相比,合成的凸包是非常小的顶点。
有什么想法吗?
答案 0 :(得分:2)
这不完整,但它可能会给你一些想法。
如果N< 3.生成具有N个顶点的单位圆,并随机旋转[0..90]度。
从原点向外随机挤出每个顶点,并使用每对相邻顶点和原点之间的叉积符号来确定凸度。这是在速度和质量之间进行权衡的步骤。
设置顶点后,找到原点最大的顶点。将每个顶点除以该幅度以对多边形进行标准化,然后将其向上缩放(C / 2)。转换为(C / 2,C / 2)并转回整数。
答案 1 :(得分:1)
一个简单的算法是:
答案 2 :(得分:1)
如果有人对它的Python端口感兴趣,在@Mangara answer之后有JAVA implementation
import random
from math import atan2
def to_convex_contour(vertices_count,
x_generator=random.random,
y_generator=random.random):
"""
Port of Valtr algorithm by Sander Verdonschot.
Reference:
http://cglab.ca/~sander/misc/ConvexGeneration/ValtrAlgorithm.java
>>> contour = to_convex_contour(20)
>>> len(contour) == 20
True
"""
xs = [x_generator() for _ in range(vertices_count)]
ys = [y_generator() for _ in range(vertices_count)]
xs = sorted(xs)
ys = sorted(ys)
min_x, *xs, max_x = xs
min_y, *ys, max_y = ys
vectors_xs = _to_vectors_coordinates(xs, min_x, max_x)
vectors_ys = _to_vectors_coordinates(ys, min_y, max_y)
random.shuffle(vectors_ys)
def to_vector_angle(vector):
x, y = vector
return atan2(y, x)
vectors = sorted(zip(vectors_xs, vectors_ys),
key=to_vector_angle)
point_x = point_y = 0
min_polygon_x = min_polygon_y = 0
points = []
for vector_x, vector_y in vectors:
points.append((point_x, point_y))
point_x += vector_x
point_y += vector_y
min_polygon_x = min(min_polygon_x, point_x)
min_polygon_y = min(min_polygon_y, point_y)
shift_x, shift_y = min_x - min_polygon_x, min_y - min_polygon_y
return [(point_x + shift_x, point_y + shift_y)
for point_x, point_y in points]
def _to_vectors_coordinates(coordinates, min_coordinate, max_coordinate):
last_min = last_max = min_coordinate
result = []
for coordinate in coordinates:
if _to_random_boolean():
result.append(coordinate - last_min)
last_min = coordinate
else:
result.append(last_max - coordinate)
last_max = coordinate
result.extend((max_coordinate - last_min,
last_max - max_coordinate))
return result
def _to_random_boolean():
return random.getrandbits(1)
答案 3 :(得分:0)
您的初始方法是正确的 - 计算凸包是您满足随机性,凸度和整数的唯一方法。
我能想到优化算法以获得“更多分数”的唯一方法是将它们围绕一个圆而不是完全随机地组织。您的点应该更可能靠近广场的“边缘”而不是靠近中心。在中心,概率应为〜0,因为多边形必须是凸的。
一个简单的选择是为您的点设置最小半径 - 可能是C / 2或C * 0.75。计算C方格的中心,如果一个点太近,则将其移离中心,直到达到最小距离。
答案 4 :(得分:0)
这是我所知道的最快的算法,它以相同的概率生成每个凸多边形。输出正好有N个顶点,运行时间为O(N log N),因此它可以非常快速地生成大型多边形。
X
和Y
,其中包含0到C之间的N个随机整数。确保没有重复项。X
和Y
并存储其最大和最小元素。X1
和X2
,以及Y1
和Y2
。minX
,X1
和X2
,maxX
结尾,等)。X1[i + 1] - X1[i]
),撤消第二组(X2[i] - X2[i + 1]
)的顺序。将它们存储在列表XVec
和YVec
。YVec
并将每对XVec[i]
和YVec[i]
视为2D向量。此处提供动画和Java实现:Generating Random Convex Polygons。
该算法基于Pavel Valtr的一篇论文:“Probability that n random points are in convex position。”离散& Computational Geometry 13.1(1995):637-643。
答案 5 :(得分:0)
由于@Mangara's answer和@Azat's answer的帮助,我也建立了ruby端口:
#!/usr/bin/env ruby
# frozen_string_literal: true
module ValtrAlgorithm
module_function def random_polygon(length)
raise ArgumentError, "length should be > 2" unless length > 2
min_x, *xs, max_x = Array.new(length) { rand }.sort
min_y, *ys, max_y = Array.new(length) { rand }.sort
# Divide the interior points into two chains and
# extract the vector components.
vec_xs = to_random_vectors(xs, min_x, max_x)
vec_ys = to_random_vectors(ys, min_y, max_y).
# Randomly pair up the X- and Y-components
shuffle
# Combine the paired up components into vectors
vecs = vec_xs.zip(vec_ys).
# Sort the vectors by angle, in a counter clockwise fashion. Remove the
# `-` to make it clockwise.
sort_by { |x, y| - Math.atan2(y, x) }
# Lay them end-to-end
point_x = point_y = 0
min_polygon_x = min_polygon_y = 0
points = []
vecs.each do |vec_x, vec_y|
points.append([vec_x, vec_y])
point_x += vec_x
point_y += vec_y
min_polygon_x = [min_polygon_x, point_x].min
min_polygon_y = [min_polygon_y, point_y].min
end
shift_x = min_x - min_polygon_x
shift_y = min_y - min_polygon_y
result = points.map { |point_x, point_y| [point_x + shift_x, point_y + shift_y] }
# Append first point to make it a valid linear ring
result << result.first
end
private def to_random_vectors(coordinates, min, max)
last_min = last_max = min
ary = []
coordinates.each do |coordinate|
if rand > 0.5
ary << coordinate - last_min
last_min = coordinate
else
ary << last_max - coordinate
last_max = coordinate
end
end
ary << max - last_min << last_max - max
end
end
答案 6 :(得分:0)
这是另一个使用 numpy 的 Valtr 算法版本。 :)
import numpy as np
import numpy.typing and npt
import random
def generateConvex(n: int) -> npt.NDArray[np.float64]:
'''
Generate convex shappes according to Pavel Valtr's 1995 alogrithm. Ported from
Sander Verdonschot's Java version, found here:
https://cglab.ca/~sander/misc/ConvexGeneration/ValtrAlgorithm.java
'''
# initialise random coordinates
X_rand = np.sort(np.random.random(n))
Y_rand = np.sort(np.random.random(n))
X_new = np.zeros(n)
Y_new = np.zeros(n)
# divide the interior points into two chains
lastTop = lastBot = X_rand[0]
lastLeft = lastRight = Y_rand[0]
for i in range(1, n - 1):
if random.getrandbits(1):
X_new[i] = X_rand[i] - lastTop
lastTop = X_rand[i]
Y_new[i] = Y_rand[i] - lastLeft
lastLeft = Y_rand[i]
else:
X_new[i] = lastBot - X_rand[i]
lastBot = X_rand[i]
Y_new[i] = lastRight - Y_rand[i]
lastRight = Y_rand[i]
X_new[0] = X_rand[n - 1] - lastTop
X_new[n - 1] = lastBot - X_rand[n - 1]
Y_new[0] = Y_rand[n - 1] - lastLeft
Y_new[n - 1] = lastRight - Y_rand[n - 1]
# randomly combine x and y, and sort by polar angle
np.random.shuffle(Y_new)
vertices = np.stack((X_new, Y_new), axis=-1)
vertices = vertices[np.argsort(np.arctan2(vertices[:, 1], vertices[:, 0]))]
# arrange the points end to end to form a polygon
x_accum = y_accum = 0
for i, [x, y] in enumerate(vertices):
vertices[i] = [x_accum, y_accum]
x_accum += x
y_accum += y
# move the polygon to the original min and max coordinates
vertices[:, 0] += X_rand[0] - np.min(vertices[:, 0]
vertices[:, 1] += Y_rand[0] - np.min(vertices[:, 1]
return vertices