这在时间和内存方面提供了一种O(1)方法。
基本原理
接受的答案和其他一些答案似乎取决于生成所有可能坐标列表的必要性,或者重新计算,直到有一个可接受的解决方案。这两种方法都需要花费更多的时间和内存。
注意,根据坐标生成的一致性要求,有不同的解决方案,如下所示。
第一次尝试
随机选择右下方指定的坐标import random
# set bounding boxes
maxx=1000
maxy=800
blocked_box = [(500, 250), (100, 75)]
# generate left/right, top/bottom and choose as you like
def gen_rand_limit(p1, dim):
x1, y1 = p1
w, h = dim
x2, y2 = x1 + w, y1 + h
left = random.randrange(0, x1)
right = random.randrange(x2+1, maxx-1)
top = random.randrange(0, y1)
bottom = random.randrange(y2, maxy-1)
return random.choice([left, right]), random.choice([top, bottom])
# check boundary conditions are met
def check(x, y, p1, dim):
x1, y1 = p1
w, h = dim
x2, y2 = x1 + w, y1 + h
assert 0 <= x <= maxx, "0 <= x(%s) <= maxx(%s)" % (x, maxx)
assert x1 > x or x2 < x, "x1(%s) > x(%s) or x2(%s) < x(%s)" % (x1, x, x2, x)
assert 0 <= y <= maxy, "0 <= y(%s) <= maxy(%s)" %(y, maxy)
assert y1 > y or y2 < y, "y1(%s) > y(%s) or y2(%s) < y(%s)" % (y1, y, y2, y)
# sample
points = []
for i in xrange(1000):
x,y = gen_rand_limit(*blocked_box)
check(x, y, *blocked_box)
points.append((x,y))
结果
给定OP中概述的约束条件,这实际上会根据需要在指定矩形(红色)周围生成随机坐标(蓝色),但忽略了矩形外部但在矩形相应x或y维度内的任何有效点:
^{pr2}$
改进
这很容易通过限制x或y坐标来解决(请注意,check不再有效,请注释以运行此部分):def gen_rand_limit(p1, dim):
x1, y1 = p1
w, h = dim
x2, y2 = x1 + w, y1 + h
# should we limit x or y?
limitx = random.choice([0,1])
limity = not limitx
# generate x, y O(1)
if limitx:
left = random.randrange(0, x1)
right = random.randrange(x2+1, maxx-1)
x = random.choice([left, right])
y = random.randrange(0, maxy)
else:
x = random.randrange(0, maxx)
top = random.randrange(0, y1)
bottom = random.randrange(y2, maxy-1)
y = random.choice([top, bottom])
return x, y
调整随机偏差
正如在评论中指出的,这个解决方案存在着对矩形行/列之外的点的偏差。下面通过给每个坐标相同的概率来修正原则上的:def gen_rand_limit(p1, dim):
x1, y1 = p1Final solution -
w, h = dim
x2, y2 = x1 + w, y1 + h
# generate x, y O(1)
# --x
left = random.randrange(0, x1)
right = random.randrange(x2+1, maxx)
withinx = random.randrange(x1, x2+1)
# adjust probability of a point outside the box columns
# a point outside has probability (1/(maxx-w)) v.s. a point inside has 1/w
# the same is true for rows. adjupx/y adjust for this probability
adjpx = ((maxx - w)/w/2)
x = random.choice([left, right] * adjpx + [withinx])
# --y
top = random.randrange(0, y1)
bottom = random.randrange(y2+1, maxy)
withiny = random.randrange(y1, y2+1)
if x == left or x == right:
adjpy = ((maxy- h)/h/2)
y = random.choice([top, bottom] * adjpy + [withiny])
else:
y = random.choice([top, bottom])
return x, y
下面的图中有10000个点来说明点的统一放置(覆盖方框边界的点是由于点的大小)。
免责声明:请注意,此图将红色框置于中间位置,这样top/bottom,left/right之间的概率相同。因此,调整是相对于分块框的,而不是针对图形的所有区域。最终的解决方案需要分别调整每一个的概率。
更简单的解决方案,但对问题稍加修改
原来,调整坐标系不同区域的概率是相当棘手的。经过一番思考,我想出了一个稍加修改的方法:
认识到在任何一个二维坐标系中,一个矩形将该区域划分为N个子区域(如果是问题,N=8),在那里可以选择一个有效的坐标。这样看,我们可以将有效的子区域定义为坐标框。然后我们可以随机选择一个长方体,并从该长方体中随机选择一个坐标:def gen_rand_limit(p1, dim):
x1, y1 = p1
w, h = dim
x2, y2 = x1 + w, y1 + h
# generate x, y O(1)
boxes = (
((0,0),(x1,y1)), ((x1,0),(x2,y1)), ((x2,0),(maxx,y1)),
((0,y1),(x1,y2)), ((x2,y1),(maxx,y2)),
((0,y2),(x1,maxy)), ((x1,y2),(x2,maxy)), ((x2,y2),(maxx,maxy)),
)
box = boxes[random.randrange(len(boxes))]
x = random.randrange(box[0][0], box[1][0])
y = random.randrange(box[0][1], box[1][1])
return x, y
注意这并不是一般化的,因为阻塞的框可能不在中间,因此boxes看起来会不同。因此,在每个框中以相同的概率选择,我们得到每个框中相同的点数。很明显,小盒子里的密度更高:
如果要求在所有可能的坐标系中生成均匀分布,则解决方案是计算boxes,使每个方框的大小与阻塞框的大小相同。基督教青年会