算法:距离变换 - 任何更快的算法?

时间:2013-05-06 16:10:53

标签: algorithm optimization

我试图解决距离变换问题(使用曼哈顿的距离)。基本上,给出0和1和1的矩阵,程序必须将每个位置的距离分配给最接近的1。例如,对于这个

0000
0100
0000
0000

距离变换矩阵

2123
1012
2123
3234

我脑子里的可能解决方案是:

最慢​​的(最慢的,因为我试图实现它们 - 它们在很大的矩阵上滞后):

  1. 蛮力 - 对于该程序读取的每1个,从开始到结束相应地改变距离。

  2. 广度优先搜索0' s - 每0,程序查找最近的1个内部。

  3. 与2相同,但从1开始标记内外的每个距离。

  4. 快得多(从其他人的代码中读取)

    广度优先搜索1&#39>

    1. Assign all values in the distance matrix to -1 or very big value.
    2. While reading matrix, put all positions of 1's into queue.
    3. While queue is not empty
        a. Dequeue position - let it be x
        b. For each position around x (that has distance 1 from it)
            if position is valid (does not exceed matrix dimensions) then
                if distance is not initialized or is greater than (distance of x) + 1 then
                    I. distance = (distance of x) + 1
                    II. enqueue position into queue
    
  5. 我想问一下这个问题是否有更快的解决方案。我试图搜索距离变换的算法,但大多数都处理欧几里德距离。

    提前致谢。

2 个答案:

答案 0 :(得分:5)

广度优先搜索将执行Θ(n*m)次操作,其中nm是矩阵的宽度和高度。

您需要输出Θ(n*m)个数字,因此从理论的角度来看,您的速度不会快于此。

我假设您对涉及缓存和此类优化的讨论不感兴趣。


请注意,此解决方案适用于更有趣的情况。例如,想象同样的问题,但可能有不同的“来源”:

00000
01000
00000
00000
00010

使用BFS,您将获得具有相同时间复杂度的以下距离 - 最近源:

21234
10123
21223
32212
32101

但是,对于单一来源,还有另一种解决方案可能在实践中具有稍好的性能(即使复杂性仍然相同)。

之前,让我们观察以下属性。

属性:如果source位于(a,b),则点(x,y)具有以下曼哈顿距离:

d(x, y) = abs(x - a) + abs(y - b)

这应该很容易证明。所以另一种算法是:

for r in rows
  for c in cols
    d(r, c) = abc(r - a) + abs(c - b)

非常简短。


除非您编写并测试它,否则没有简单的方法来比较这两种算法。假设有效的有界队列实现(带有数组),每个单元有以下主要操作:

  • BFS:队列插入/删除,每个节点访问5次(邻居4次,队列退出一次)
  • 直接公式:两个减法和两个if s

这实际上取决于编译器及其优化以及特定的CPU和内存架构,以确定哪些性能会更好。

那就是说,我建议你选择一个看似简单的东西。但请注意,对于多个源,在第二个解决方案中,您需要在阵列上进行多次传递(或在一次传递中进行多次距离计算),对于足够多的源,这肯定会比BFS的性能更差。

答案 1 :(得分:2)

你根本不需要队列或类似的东西。请注意,如果(i,j)距离(k,l)的距离为d,则实现该距离的一种方法是向左或向右| i-k |时间然后上升或下降| j-l |次。

因此,使用大数字初始化矩阵,并在输入中有1的任何地方粘贴零。现在做这样的事情:

for (i = 0; i < sx-1; i++) {
  for (j = 0; j < sy-1; j++) {
    dist[i+1][j] = min(dist[i+1][j], dist[i][j]+1);
    dist[i][j+1] = min(dist[i][j+1], dist[i][j]+1);
  }
  dist[i][sy-1] = min(dist[i][sy-1], dist[i][sy-2]+1);
}
for (j = 0; j < sy-1; j++) {
  dist[sx-1][j] = min(dist[sx-1][j], dist[sx-2][j]+1);
}

此时,您已找到所有仅涉及向下或向右的最短路径。如果你做同样的事情上升和离开,dist[i][j]将给你从输入矩阵中的(i,j)到最近的1的距离。