Find points that are equidistant from 4 points

Question:

This code needs a very long time to find a point. Is there any better way to find a point that is equidistant from 4 given points? A tolerance of 0.5 is fine.

import numpy as np

# Define the four 3D points as (x, y, z) coordinates
point1 = np.array([1, 2, 3])
point2 = np.array([3, 4, 5])
point3 = np.array([5, 6, 7])
point4 = np.array([7, 8, 9])

# Set a tolerance for distance equality
tolerance = 0.5

# Calculate the initial average point
average_point = np.mean([point1, point2, point3, point4], axis=0)

while True:
    distances = np.linalg.norm(average_point - np.array([point1, point2, point3, point4]), axis=1)
    max_distance = np.max(distances)
    min_distance = np.min(distances)

    if max_distance - min_distance <= tolerance:
        break

    # Update the average point by moving it closer to the farthest point
    farthest_point = np.argmax(distances)
    average_point = average_point + 0.5 * (point1 - average_point)

print("Average point with relatively equal distances:", average_point)

Asked By: Binh Thien

||

Answers:

Why are you using a loop for that? Let me show you how to do this in 2D:

enter image description here

You need to find the point on the same distance from points A, B and C. In order to do that, you need the perpendicular bisector of A and B, you also take the perpendicular bisector of A and C, and the intersection of both gives you the point D, which is the solution.
In order to prove the latter, the circle is drawn with D as the center. As you can see, that circle contains the points A, B and C, proving that indeed D lays on the same distance from the three starting points.

Just expand this reasoning to 3D and you have your solution, and as you see, no loops are needed for this.

Answered By: Dominique

This one based on the intersection of two bisectors does not work well. @Dominique can you please take a look?

import numpy as np

# Define the four 3D points as (x, y, z) coordinates
point1 = np.array([10, 2, 3])
point2 = np.array([3, 40, 5])
point3 = np.array([50, 6, 7])
point4 = np.array([7, 8, 90])

# Calculate the midpoint of a line segment between two points
def midpoint(point1, point2):
    return (point1 + point2) / 2

# Calculate the direction vector of the line
def direction_vector(point1, point2):
    return point2 - point1

# Calculate the negative reciprocal of the slopes in 3D
def perpendicular_slopes_3d(point1, point2):
    return -direction_vector(point1, point2)

# Calculate the equation of the perpendicular bisector in 3D
def perpendicular_bisector_3d(point1, point2):
    mid = midpoint(point1, point2)
    perp_slope = perpendicular_slopes_3d(point1, point2)
    return (mid, perp_slope)

# Calculate the perpendicular bisectors for each pair of points
bisector12 = perpendicular_bisector_3d(point1, point2)
bisector34 = perpendicular_bisector_3d(point3, point4)

# Solve for the intersection point in 3D
mid1, dir1 = bisector12
mid2, dir2 = bisector34

# Calculate the intersection point by finding the point of closest approach between the two lines
dir1_normalized = dir1 / np.linalg.norm(dir1)
dir2_normalized = dir2 / np.linalg.norm(dir2)

# Parameters for the lines
t1 = np.dot(mid2 - mid1, dir1_normalized) / np.dot(dir1_normalized, dir1_normalized)
t2 = np.dot(mid2 - mid1, dir2_normalized) / np.dot(dir2_normalized, dir2_normalized)

intersection_point = mid1 + t1 * dir1_normalized

# Calculate distances between the intersection point and all given points in 3D
distances = [np.linalg.norm(intersection_point - point) for point in [point1, point2, point3, point4]]

# Print the distances
for i, point in enumerate([point1, point2, point3, point4]):
    print(f"Distance between intersection and point {i + 1}: {distances[i]}")

# Print the intersection point
print("Intersection point in 3D:", intersection_point)

Its results are

Distance between intersection and point 1: 3.9156307702153543
Distance between intersection and point 2: 34.77545347409085
Distance between intersection and point 3: 40.885458802402674
Distance between intersection and point 4: 86.8545619404054
Intersection point in 3D: [9.29158317 5.84569138 3.20240481]
Answered By: Binh Thien

The problem has no solution in general, but an approximation can be obtained using a minimization approach.

If the points are:

>>> points = np.array([[10,  2,  3],
...                    [ 3, 40,  5],
...                    [50,  6,  7],
...                    [ 7,  8, 90]])

An error function can be defined as the variance of the distances:

>>> def distance_error(x, pts):
...     dist = np.sqrt(np.sum((pts - x) ** 2, axis=1))
...     return np.var(dist)

Then the best point p can be found by minimizing the error function:

>>> from scipy.optimize import minimize
>>> p = minimize(distance_error, x0=(0,0,0), args=points).x
>>> p
array([24.10963833, 22.04056223, 45.86305227])

And the distances are:

>>> np.sqrt(np.sum((points - p)**2, axis=1))
array([49.3755737 , 49.37557368, 49.37557388, 49.37557358])
Answered By: aerobiomat
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.