[英]How to implement in Python a function to compute the Euclidean distance between two arbitrary points on a torus
Given a 10x10 grid (2d-array) filled randomly with numbers, either 0, 1
or 2
.给定一个 10x10 网格(二维数组),随机填充数字,
0, 1
或2
。 How can I find the Euclidean distance (the l2-norm of the distance vector) between two given points considering periodic boundaries?考虑到周期性边界,如何找到两个给定点之间的欧几里得距离(距离向量的 l2 范数)?
Let us consider an arbitrary grid point called centre
.让我们考虑一个称为
centre
的任意网格点。 Now, I want to find the nearest grid point containing the same value as centre
.现在,我想找到包含与
centre
相同值的最近网格点。 I need to take periodic boundaries into account, such that the matrix/grid can be seen rather as a torus instead of a flat plane.我需要考虑周期性边界,这样矩阵/网格可以被看作是一个圆环而不是一个平面。 In that case, say the
centre = matrix[0,2]
, and we find that there is the same number in matrix[9,2]
, which would be at the southern boundary of the matrix.在这种情况下,假设
centre = matrix[0,2]
,我们发现matrix[9,2]
中存在相同的数字,它将位于矩阵的南边界。 The Euclidean distance computed with my code would be for this example np.sqrt(0**2 + 9**2) = 9.0
.使用我的代码计算的欧几里得距离将用于此示例
np.sqrt(0**2 + 9**2) = 9.0
。 However, because of periodic boundaries, the distance should actually be 1
, because matrix[9,2]
is the northern neighbour of matrix[0,2]
.但是,由于周期性边界,距离实际上应该是
1
,因为matrix[9,2]
是matrix[0,2]
的北邻。 Hence, if periodic boundary values are implemented correctly, distances of magnitude above 8 should not exist.因此,如果正确实现了周期性边界值,则不应存在幅度大于 8 的距离。
So, I would be interested on how to implement in Python a function to compute the Euclidean distance between two arbitrary points on a torus by applying a wrap-around for the boundaries.因此,我会对如何在 Python 中实现 function 感兴趣,以通过对边界应用环绕来计算环面上两个任意点之间的欧几里得距离。
import numpy as np
matrix = np.random.randint(0,3,(10,10))
centre = matrix[0,2]
#rewrite the centre to be the number 5 (to exclude itself as shortest distance)
matrix[0,2] = 5
#find the points where entries are same as centre
same = np.where((matrix == centre) == True)
idx_row, idx_col = same
#find distances from centre to all values which are of same value
dist = np.zeros(len(same[0]))
for i in range(0,len(same[0])):
delta_row = same[0][i] - 0 #row coord of centre
delta_col = same[1][i] - 2 #col coord of centre
dist[i] = np.sqrt(delta_row**2 + delta_col**2)
#retrieve the index of the smallest distance
idx = dist.argmin()
print('Centre value: %i. The nearest cell with same value is at (%i,%i)'
% (centre, same[0][idx],same[1][idx]))
For each axis, you can check whether the distance is shorter when you wrap around or when you don't.对于每个轴,您可以检查环绕时或不环绕时距离是否较短。 Consider the row axis, with rows
i
and j
.考虑行轴,行
i
和j
。
abs(i - j)
.abs(i - j)
。10 - abs(i - j)
.10 - abs(i - j)
。 In your example with i == 0
and j == 9
you can check that this correctly produces a distance of 1.i == 0
和j == 9
的示例中,您可以检查这是否正确产生了 1 的距离。 Then simply take whichever is smaller:然后只需取较小的那个:
delta_row = same[0][i] - 0 #row coord of centre
delta_row = min(delta_row, 10 - delta_row)
And similarly for delta_column
.同样对于
delta_column
。
The final dist[i]
calculation needs no changes.最终的
dist[i]
计算不需要更改。
I have a working 'sketch' of how this could work.我有一个关于它如何工作的工作“草图”。 In short, I calculate the distance 9 times, 1 for the normal distance, and 8 shifts to possibly correct for a closer 'torus' distance.
简而言之,我计算了 9 次距离,1 次表示正常距离,8 次移位可能纠正更近的“环面”距离。
As n
is getting larger, the calculation costs can go sky high as the numbers go up.随着
n
越来越大,计算成本可能会随着数字 go 的增加而变得非常高。 But, the torus effect, is probably not needed as there is always a point nearby without 'wrap around'.但是,可能不需要环面效应,因为附近总是有一个点没有“环绕”。
You can easily test this, because for a grid of size 1, if a point is found of distance 1/2 or closer, you know there is not a closer torus point (right?)您可以轻松地对此进行测试,因为对于大小为 1 的网格,如果找到距离为 1/2 或更近的点,您就知道没有更近的圆环点(对吗?)
import numpy as np
n=10000
np.random.seed(1)
A = np.random.randint(low=0, high=10, size=(n,n))
I create 10000x10000 points, and store the location of the 1's in ONES
.我创建 10000x10000 点,并将 1 的位置存储在
ONES
中。
ONES = np.argwhere(A == 0)
Now I define my torus distance, which is trying which of the 9 mirrors is the closest.现在我定义了我的环面距离,即尝试 9 个镜子中的哪一个最接近。
def distance_on_torus( point=[500,500] ):
index_diff = [[1],[1],[0],[0],[0,1],[0,1],[0,1],[0,1]]
coord_diff = [[-1],[1],[-1],[1],[-1,-1],[-1,1],[1,-1],[1,1]]
tree = BallTree( ONES, leaf_size=5*n, metric='euclidean')
dist, indi = tree.query([point],k=1, return_distance=True )
distances = [dist[0]]
for indici_to_shift, coord_direction in zip(index_diff, coord_diff):
MIRROR = ONES.copy()
for i,shift in zip(indici_to_shift,coord_direction):
MIRROR[:,i] = MIRROR[:,i] + (shift * n)
tree = BallTree( MIRROR, leaf_size=5*n, metric='euclidean')
dist, indi = tree.query([point],k=1, return_distance=True )
distances.append(dist[0])
return np.min(distances)
%%time
distance_on_torus([2,3])
It is slow, the above takes 15 minutes.... For n = 1000
less than a second.它很慢,上面需要 15 分钟....对于
n = 1000
不到一秒。
A optimisation would be to first consider the none-torus distance, and if the minimum distance is possibly not the smallest, calculate with only the minimum set of extra 'blocks' around.优化将首先考虑非环面距离,如果最小距离可能不是最小的,则仅使用最小的额外“块”集进行计算。 This will greatly increase speed.
这将大大提高速度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.