生成多个随机(x,y)坐标,不包括重复?

Posted

技术标签:

【中文标题】生成多个随机(x,y)坐标,不包括重复?【英文标题】:Generating multiple random (x, y) coordinates, excluding duplicates? 【发布时间】:2013-11-09 05:06:09 【问题描述】:

我想生成一个从 0 到 2500 的束 (x, y) 坐标,它不包括彼此相距 200 以内的点而无需递归。

现在我让它检查所有先前值的列表,看看是否有任何值与其他值足够远。这真的很低效,如果我需要生成大量的点,这需要很长时间。

那么我该怎么做呢?

【问题讨论】:

一种方法是用所有可用点填充列表。然后从您的列表中选择一个随机元素并删除所有离它太近的点。不过,这在内存方面会非常昂贵。实现这一点的一种方法是 2500 x 2500 的 0-1 数组,表示有效或无效。生成 x 和 y,如果 points[x][y] == 0 则将其设置为 1,并将所有内容设置在 x-100 到 x+100 和 y-100 到 y+100 的范围内。如果你能侥幸成功的话,删除正方形中的点会比 200 半径圆中的点更容易。 这些点是随机的吗?随机性如何? 绘制一个网格,随机放置原点。然后取每个正方形的中心点。完成。 @ColonelPanic 这不会是一个非常随机的点分布。每个点将有 4 个点与之等距。 【参考方案1】:

这是 Hank Ditton 建议的一个变体,在时间和记忆方面应该更有效,尤其是当您从所有可能的点中选择相对较少的点时。这个想法是,每当生成一个新点时,将其 200 个单位内的所有内容添加到一组要排除的点中,并根据这些点检查所有新生成的点。

import random

radius = 200
rangeX = (0, 2500)
rangeY = (0, 2500)
qty = 100  # or however many points you want

# Generate a set of all points within 200 of the origin, to be used as offsets later
# There's probably a more efficient way to do this.
deltas = set()
for x in range(-radius, radius+1):
    for y in range(-radius, radius+1):
        if x*x + y*y <= radius*radius:
            deltas.add((x,y))

randPoints = []
excluded = set()
i = 0
while i<qty:
    x = random.randrange(*rangeX)
    y = random.randrange(*rangeY)
    if (x,y) in excluded: continue
    randPoints.append((x,y))
    i += 1
    excluded.update((x+dx, y+dy) for (dx,dy) in deltas)
print randPoints

【讨论】:

不错!它在空间方面当然更有效,但在时间方面呢?在这两种算法中,我们都在生成一个点,将相同数量的点设置为排除,如果该点无效,则可能丢弃该点。 这似乎不起作用。生成的许多点相互重叠。 @user2901745:真的吗?输出看起来与我预期的一样。您的意思是代码在一次运行中生成重复点吗?你能举个例子吗? 是的,您可以看到here 点是重叠的。我正在使用生成的列表在此图像周围粘贴随机字符(尽管我无法使此字体正常工作,但这是另一个问题)。它们不是重复的,但它们之间的距离在 200 像素以内。这是我发现的两个点,它们都是在同一个调用中生成的:(1975, 2169), (1917, 2191) @user2901745:啊,我明白我做错了什么。我忘了在第 13 行平方 radius,使有效半径实际上约为 14.1。【参考方案2】:

我会过度生成点 target_N &lt; input_N,并使用 KDTree 过滤它们。例如:

import numpy as np
from scipy.spatial import KDTree
N   = 20
pts = 2500*np.random.random((N,2))

tree = KDTree(pts)
print tree.sparse_distance_matrix(tree, 200)

会给我彼此“接近”的分数。从这里应用任何过滤器应该很简单:

  (11, 0)   60.843426339
  (0, 11)   60.843426339
  (1, 3)    177.853472309
  (3, 1)    177.853472309

【讨论】:

【参考方案3】:

一些选项:

使用您的算法,但使用 kd-tree 来实现它,这将加快最近邻查找的速度 在 [0, 2500]^2 正方形上构建规则网格,并以网格中每个交点为中心的二维正态分布随机“摇动”所有点 绘制大量随机点,然后应用k-means 算法并仅保留质心。它们将彼此相距很远,并且该算法虽然是迭代的,但可以比您的算法更快地收敛。

【讨论】:

【参考方案4】:

已经回答了这个问题,但它与我的工作非常相干,所以我尝试了一下。我实现了this note 中描述的算法,我发现它与this blog post 链接。不幸的是,它并不比其他提议的方法快,但我确信需要进行优化。

import numpy as np
import matplotlib.pyplot as plt

def lonely(p,X,r):
    m = X.shape[1]
    x0,y0 = p
    x = y = np.arange(-r,r)
    x = x + x0
    y = y + y0

    u,v = np.meshgrid(x,y)

    u[u < 0] = 0
    u[u >= m] = m-1
    v[v < 0] = 0
    v[v >= m] = m-1

    return not np.any(X[u[:],v[:]] > 0)

def generate_samples(m=2500,r=200,k=30):
    # m = extent of sample domain
    # r = minimum distance between points
    # k = samples before rejection
    active_list = []

    # step 0 - initialize n-d background grid
    X = np.ones((m,m))*-1

    # step 1 - select initial sample
    x0,y0 = np.random.randint(0,m), np.random.randint(0,m)
    active_list.append((x0,y0))
    X[active_list[0]] = 1

    # step 2 - iterate over active list
    while active_list:
        i = np.random.randint(0,len(active_list))
        rad = np.random.rand(k)*r+r
        theta = np.random.rand(k)*2*np.pi

        # get a list of random candidates within [r,2r] from the active point
        candidates = np.round((rad*np.cos(theta)+active_list[i][0], rad*np.sin(theta)+active_list[i][1])).astype(np.int32).T

        # trim the list based on boundaries of the array
        candidates = [(x,y) for x,y in candidates if x >= 0 and y >= 0 and x < m and y < m]

        for p in candidates:
            if X[p] < 0 and lonely(p,X,r):
                X[p] = 1
                active_list.append(p)
                break
        else:
            del active_list[i]

    return X

X = generate_samples(2500, 200, 10)
s = np.where(X>0)
plt.plot(s[0],s[1],'.')

结果:

【讨论】:

这将是一组有趣的点,可用于进行数据分析以寻找趋势或模式。对我来说这似乎不是完全随机的,但话说回来,它们必须彼此相距 200 分这一事实可能会导致看起来像一个模式,尽管它可能不是。 我知道它已经过时了,但我无法让您的代码在 Python 3.5.1 中运行。我从math 添加了一些导入,但至少这是一次尝试。我还是个初学者。 @RickHenderson 我刚刚更新了代码并在 Python 3.5 中对其进行了测试——它对我有用!【参考方案5】:

根据链接,来自 aganders3 的方法称为泊松圆盘采样。您可能能够找到使用本地网格搜索来查找“重叠”的更有效的实现。例如Poisson Disc Sampling。因为你在约束系统,所以它不可能是完全随机的。平面中具有均匀半径的圆的最大堆积约为 90%,并且当圆排列成完美的六边形阵列时实现。随着您请求的点数接近理论限制,生成的排列将变得更加六边形。以我的经验,使用这种方法很难达到 60% 以上的均匀圆圈填充。

【讨论】:

以上是关于生成多个随机(x,y)坐标,不包括重复?的主要内容,如果未能解决你的问题,请参考以下文章

C#中对象的随机x和y坐标[重复]

C#如何生成随机不重复的数字

Python:在x和y之间生成随机数,它是5的倍数[重复]

在画布中随机生成对象,不重复或重叠

在 Y 和 Z 之间生成 X 个随机整数? [复制]

VBS产生随机不重复的数字