我正面临着一个问题,我陷入了微积分和约束之间......
我有一个点网格,有点均匀地分布在3D网格上(第一张图片)。
注意:你没有看到它,但有一个垂直于网格的第3轴 - 有125个点(每边5 x 5 x 5)
3D grid with dots distributed ~ equally
我的目标是,通过噪声统计给出一定距离(我们将在这种情况下取0.03),用这些条件填充网格:
以下是填充网格的示例:
Badly filled grid (still in 3D)
问题是,这里有很多我想删除的空白区域。
有关详细信息,此处是从一个点到最近邻居的所有距离的图表:
Histogram of distances from dots their closest neighbour
红色矩形是我希望所有蓝色条形堆叠的地方,在这种情况下介于0.03 - 10%和0.03 + 10%之间。
主要的问题是,因为它是随机生成的,所以我必须生成很多点,希望即使只有一个点符合约束。这是一个相对“简单的案例”,因为之后,我将不得不将其应用于:
硬案例
我不是python中的一个破解,我只知道基础知识(numpy,matplotlib.pyplot),并且对图表,数组,循环非常熟悉,但是我的代码没有针对那些微积分的时间进行优化任务类型:
如果有人有一个简单的想法或一些建议,那就太好了,我也希望我做得很好解释(我试图保留最重要的东西,并尝试用适当的英文写作:))。
感谢阅读,
森。
编辑:此处简化代码,因为它可能有助于更轻松地回答! 我直接添加了非均匀分布的网格(A,B)。
import numpy as np
import matplotlib.pyplot as plt
plt.close("all")
A = np.random.rand(100)
B = np.random.rand(100)
plt.figure()
plt.plot(A, B,".")
plt.grid()
Nfillers = 10000
SNRD = 0.03
for i in range(Nfillers):
a, b = np.random.rand(2)
A = np.hstack((A, np.atleast_1d(a)))
B = np.hstack((B, np.atleast_1d(b)))
DCN = 1E+308 # Distance Closest Neighbor
for j in range(len(A)): # len(A) is updated with each iteration
D1 = A[j] - a
D2 = B[j] - a
distance = np.sqrt(D1**2 + D2**2)
if distance != 0:
if distance < DCN:
DCN = distance
if DCN < 0.9*SNRD or DCN > 1.1*SNRD:
A = np.delete(A, len(A)-1)
B = np.delete(B, len(B)-1)
print i
plt.figure()
plt.plot(A, B, ".")
plt.grid()
编辑2:更快的代码:
import numpy as np
import matplotlib.pyplot as plt
A = np.random.rand(100)
B = np.random.rand(100)
plt.figure()
plt.plot(A, B,".")
plt.grid()
Nfillers = 100000
SNRD = 0.03
for i in range(Nfillers):
a, b = np.random.rand(2)
DCN = np.min(np.sqrt((A - a)**2 + (B - b)**2))
if DCN > 0.9*SNRD and DCN < 1.1*SNRD:
A = np.hstack((A, np.atleast_1d(a)))
B = np.hstack((B, np.atleast_1d(b)))
print(i)
plt.figure()
plt.plot(A, B, ".")
plt.grid()
答案 0 :(得分:0)
你没有包含任何代码,但这里有一个关于如何加速这些问题的大致想法(计算许多点之间的距离)。
将空间划分为大致与所需距离d
大小相当的块。这可以是块号到点列表E.g的映射。 blocks[i,j,k] = list_of_points
。然后,当您生成新点时,您只需要检查相邻的块。类似的东西:
new_point = random_point()
i, j, k = get_block_indices(new_point)
for ai in (i-1, i, i+1):
for aj in (j-1, j, j+1):
for ak in (k-1, k, k+1):
for other_point in blocks[ai, aj, ak]:
# check whether other_point is too close
found_close_other_point = check(new_point, other_point, d)
if found_close_other_point:
return # start again
# only reach if we haven't found a close point
try:
blocks[i, j, k].append(new_point)
except KeyError:
# create new block
blocks[i, j, k] = [new_point]
在您想要存储的块数和需要检查的邻居数之间需要进行权衡。一些额外的要点:
您不希望存储每个块,而只是在块不存在时处理(KeyError
)为空。在最糟糕的情况下,如果你懒得这样做,你将需要尽可能多的积分。
您可以使用itertools.product
来避免嵌套for循环。
如果您玩的是块的大小,您还可以引入额外的跳过条件,例如:如果您知道某个区块中的点数可能超过n
,那么您只需要检查len(block[i, j, k]) > n
。
纯python实现可能不是最快的,您可以使用http://numba.pydata.org/来查看速度,这对于这些类型的数字问题非常容易和快速,同时仍然像python一样阅读。
编辑:实施。
import itertools
import numpy as np
from math import ceil
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def compute_min_distances(ps):
"""Compute the minimum distances between all points ``ps``,
with shape (num_points, num_dims).
"""
ndim = ps.shape[-1]
p1, p2 = ps.reshape(-1, 1, ndim), ps.reshape(1, -1, ndim)
dd = np.sum((p1 - p2)**2, -1)**0.5
# want to ignore distance to self
np.fill_diagonal(dd, np.inf)
return np.min(dd, axis=0)
def get_block_indices(p, B):
"""Given B blocks, get the indices of ``p``.
"""
return [int(c * B) for c in p]
def init_blocks(ps, B):
"""Set up the initial block mapping.
"""
blocks = {}
for p in ps:
i, j, k = get_block_indices(p, B)
try:
blocks[i, j, k].append(p)
except KeyError:
blocks[i, j, k] = [p]
return blocks
def distance(p1, p2):
"""Distance bewtween two points.
"""
return sum((a - b)**2 for a, b in zip(p1, p2))**0.5
def gen_close_points(p0s, D=0.03, N=1000):
"""Takes ``p0s`` and generates ``N`` new points with each of
which has a closest neighbour +- 10% of ``D``.
"""
# number of blocks required so that only neigbours matter
B = ceil(1 / D)
# mapping of blocks to points
blocks = init_blocks(p0s, B)
n = 0
while n < N:
new_p = np.random.rand(3)
i, j, k = get_block_indices(new_p, B)
neighbour_inds = [(b - 1, b, b + 1) for b in (i, j, k)]
def gen_neighbours():
for ai, aj, ak in itertools.product(*neighbour_inds):
try:
# try yielding each point
yield from blocks[ai, aj, ak]
except KeyError:
# skip if empty
continue
ds = (distance(new_p, p) for p in gen_neighbours())
min_d = min(ds, default=0)
if 0.9 * D < min_d < 1.1 * D:
try:
blocks[i, j, k].append(new_p)
except KeyError:
blocks[i, j, k] = [new_p]
n += 1
# combine into array of all points
return np.concatenate(tuple(itertools.chain(blocks.values())))
让我们用一些初始点给它一个旋转:
p0s = np.random.rand(100, 3)
d0s = compute_min_distances(p0s)
plt.hist(d);
现在让我们生成10000多个点,每个点都指定最近的距离:
ps = gen_close_points(p0s, 0.03, 10000)
检查距离:
plt.hist(compute_min_distances(ps));
看起来不错!最后让我们一起绘制旧点和新点:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter3D(ps[:, 0], ps[:, 1], ps[:, 2], alpha=0.1)
ax.scatter3D(p0s[:, 0], p0s[:, 1], p0s[:, 2])
目前假设数据是3维的范围(0,1),但应该是推广它的一个不错的起点。