在3D数组中搜索满足某个谓词的最近点

时间:2017-07-04 09:19:08

标签: algorithm search multidimensional-array

我正在寻找一种枚举算法来搜索围绕给定起点的3D数组“sphering”。

给定大小为a的数组NxNxN,其中N对于某些2^kk,并且该数组中有p个点a[p] 。我正在寻找的算法应该执行以下操作:如果p满足某个谓词,算法将停止并返回q。否则,将检查下一个点q,其中p是数组中距离q'最近且尚未访问过的另一个点。如果两者都不匹配,则检查下一个q,直到最坏的情况下搜索整个数组。

通过“最接近”这里,完美的解决方案是与p具有最小欧几里德距离的点public static void main(String[] argv) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8")); System.out.println(" [x] Sent '" + message + "'"); channel.close(); connection.close(); } 。由于只需要考虑离散点,也许一些聪明的枚举算法可以使这成为可能。但是,如果这太复杂,曼哈顿最小的距离也会很好。如果有几个最近的点,那么接下来应该考虑哪一个并不重要。

是否已有可用于此任务的算法?

2 个答案:

答案 0 :(得分:1)

这是一个简单算法的伪代码,它将在增加半径的球形外壳中搜索,直到它找到一个点或者它用完了数组。让我们假设condition返回true或false并且可以访问正在测试的x,y,z坐标和数组本身,为越界坐标返回false(而不是爆炸):

def find_from_center(center, max_radius, condition) returns a point
  let radius = 0
  while radius < max_radius,
     let point = find_in_spherical_husk(center, radius, condition)
     if (point != null) return point
     radius ++
  return null

困难部分在find_in_spherical_husk内。我们有兴趣查看点

dist(center, p) >= radius AND dist(center, p) < radius+1

这将是我们对稻壳的操作定义。我们可以在O(n ^ 3)中迭代整个3D数组来寻找那些,但这在时间上会非常昂贵。一个更好的伪代码如下:

def find_in_spherical_husk(center, radius, condition)
   let z = center.z - radius // current slice height
   let r = 0 // current circle radius; maxes at equator, then decreases
   while z <= center + radius,
     let z_center = (z, center.x, point.y)  
     let point = find_in_z_circle(z_center, r)
     if (point != null) return point
     // prepare for next z-sliced cirle
     z ++
     r = sqrt(radius*radius - (z-center.z)*(z-center.z)) 

这里的想法是将每个外壳切成沿z轴的圆圈(任何轴都可以),然后分别查看每个切片。如果你正在看地球,而极点是z轴,那么你将从北向南切割。最后,您将实施find_in_z_circle(z_center, r, condition)来查看每个圈子的周长。你可以使用Bresenham circle-drawing algorithm来避免一些数学运算;但我认为与检查condition的费用相比,节省的费用可以忽略不计。

答案 1 :(得分:1)

您可以搜索增加的平方距离,这样您就不会错过一个点。这个python代码应该清楚说明:

import math
import itertools

# Calculates all points at a certain distance.
# Coordinate constraint: z <= y <= x
def get_points_at_squared_euclidean_distance(d):
    result = []
    x = int(math.floor(math.sqrt(d)))
    while 0 <= x:
        y = x
        while 0 <= y:
            target = d - x*x - y*y
            lower = 0
            upper = y + 1
            while lower < upper:
                middle = (lower + upper) / 2
                current = middle * middle
                if current == target:
                    result.append((x, y, middle))
                    break
                if current < target:
                    lower = middle + 1
                else:
                    upper = middle
            y -= 1
        x -= 1
    return result

# Creates all possible reflections of a point
def get_point_reflections(point):
    result = set()
    for p in itertools.permutations(point):
        for n in range(8):
            result.add((
                p[0] * (1 if n % 8 < 4 else -1),
                p[1] * (1 if n % 4 < 2 else -1),
                p[2] * (1 if n % 2 < 1 else -1),
            ))
    return sorted(result)

# Enumerates all points around a center, in increasing distance
def get_next_point_near(center):
    d = 0
    points_at_d = []
    while True:
        while not points_at_d:
            d += 1
            points_at_d = get_points_at_squared_euclidean_distance(d)
        point = points_at_d.pop()
        for reflection in get_point_reflections(point):
            yield (
                center[0] + reflection[0],
                center[1] + reflection[1],
                center[2] + reflection[2],
            )

# The function you asked for
def get_nearest_point(center, predicate):
    for point in get_next_point_near(center):
        if predicate(point):
            return point

# Example usage
print get_nearest_point((1,2,3), lambda p: sum(p) == 10)

基本上你从生成器消耗点,直到其中一个点满足你的谓词。