【问题标题】:Efficiently filling empty cells in 3D matrix有效填充 3D 矩阵中的空单元格
【发布时间】:2014-06-23 04:50:09
【问题描述】:

我有一个 3D“立方”矩阵,其中一些单元格已填充,而其他单元格为空。由填充单元包围的封闭区域表示中空形状。例如,基质中的细胞可以以这样的方式填充,它们一起形成空心球体的表面。现在,我想要一个有效的方法来填充这个球体的内部:如果一个单元格C0 被填充单元格在所有方向上包围(任何方向上的填充单元格不需要是C0 的直接邻居),然后填充@ 987654324@.

一种天真的方法如下:-

对于每个单元格,沿 +X、-X、+Y、-Y、+Z、-Z 方向扫描,然后查看 如果您在每个方向都遇到一个填充的单元格。

如果在每个方向都遇到填充单元格,则填充此 细胞(因为它是某种形状内部的一部分)。

如果你在一个方向上到达网格的尽头而没有遇到任何填充 单元格,则所考虑的单元格不在任何形状的内部, 并且应该保持未填充。

上述方法的复杂度为O(n^4),其中3D网格的维度为n*n*n

优化可能如下:-

如果对于一个未填充的单元格 C[x][y][z],我们遇到了一个已填充的单元格 每个在所有 6 个方向上,那么不仅 C[x][y][z] 需要 被填充,也保证我们扫描的所有单元格 刚才(即{在+X方向,所有单元格C[x][y][z],C[x+1][y][z], C[x+2][y][z], ..., 直到第一个填充的单元格},对于 -X,+Y,类似, -Y, +Z, -Z 方向)必须是某个形状内部的一部分,因此必须被填充。

另一个可能如下:-

如果对于未填充的单元格 C[x][y][z],我们不会遇到任何已填充的 比如说,+X 方向的单元格,那么不仅 C[x][y][z] 会保留 未填充,也保证我们扫描的所有单元格 刚才(即+X方向,所有单元格C[x][y][z], C[x+1][y][z], C[x+2][y][z], ..., 直到网格结束)必须是外部的一部分 因此,必须保持未填充。

有人可以提出一个更有效的方法来解决这个问题吗?即使是像上面这样可能不会降低时间复杂度的简单优化也是受欢迎的。

【问题讨论】:

  • 找到 X、Y 和 Z 的极值有帮助吗?
  • 你的空心形状有两个开口的边?喜欢this
  • @PhamTrung 不。我描述的形状是封闭的空心形状,中间没有任何孔。
  • @Nullpointer,我没听明白。请详细说明。
  • 所以你的问题有两个子问题:找到空心,然后填充它?

标签: algorithm data-structures matrix


【解决方案1】:

您正在处理 3D 泛洪填充。详见维基百科文章http://en.m.wikipedia.org/wiki/Flood_fill

【讨论】:

    【解决方案2】:

    好的,因为这是一个封闭的空心形状,我们可以简单地使用BFSDFS来解决问题。

    BFS:

    从一个空队列开始,将位于空心形状内部的任何单元格添加到队列中。从队列的顶部,弹出一个单元格,填充这个单元格并检查这个单元格的其他6个邻居,如果这个邻居没有被填充,则将其添加到队列中,否则忽略这个单元格。继续这个过程,直到队列为空。

    剩下的问题是找到一个位于空心形状内部的单元格,一个技巧是你需要找到位于形状角落的单元格,它至少有 三个填充的邻居 .

    时间复杂度是O(需要填充单元格的数量*需要检查的6个方向)

    向6方向移动的提示:

    int[] x = {0,0,0,0,1,-1};
    int[] y = {0,0,1,-1,0,0};
    int[] z = {1,-1,0,0,0,0};
    
    Point p = // point in space with three dimension x,y,z
    
    for(int i = 0; i < 6; i++){
         int a = p.x + x[i];
         int b = p.y + y[i];
         int c = p.z + z[i];
    }
    

    【讨论】:

      【解决方案3】:

      对于每个单元格,在 +X、-X、+Y、-Y、+Z、-Z 方向扫描,看看是否在每个方向都遇到了填充的单元格。

      如果在每个方向都遇到填充单元格,则填充该单元格(因为它是某些形状内部的一部分)。

      除非您只处理convex hulls,否则上述陈述不正确。下图显示有问题的点未包含在蓝色形状中,但仍会在所有 (x,y,z) 方向上相交。

      相反,要处理查找空心形状的一般情况,您可以将所有单元格添加到集合中。然后从边界单元开始。边界处的单元格如果被填充则为空心形状的一部分,否则为背景(未填充)形状的一部分。

      然后,类似于@Pham Trung 的回答,您可以向各个方向向外遍历,直到遍历形状内的所有单元格,而忽略边界处的彩色单元格。在前一个形状的边界处选择另一个单元格并重新开始该过程,直到遍历所有单元格。

      最后,您会将每个单元格标记为空心形状或背景的一部分。

      【讨论】:

        【解决方案4】:

        为了完整起见,还有两个。 YMMV 取决于很多因素。

        1.寻找表面

        如果您正在处理大量体素,一种优化可能性是找到空心的边界表面。这可以像 Pham Trung 的回答那样完成,但只接受至少填充了 6 个邻居中的一个的单元格。

        确定边界表面后,可以使用 1D 填充逐行填充,因为“内部”和“外部”方向是已知的。

        如果您有大量体素(缩放为 n^2 而不是 n^3),此方法可使集合大小更小。集合查找通常非常快,但如果集合不适合 RAM,它们就会减慢很多。

        2。切片为二维

        另一种可能性是将形状切成二维切片并逐层连接生成的空腔。然后只需要同时在内存中保存两个切片。

        主要思想是给每个单独的连接二维区域一个自己的标识符,然后找到它与相邻层中已知区域的连接。处理完所有层后,连接的 3D 区域仍然存在。

        具有挑战性的部分是找到最佳算法来连接相邻层中的 2D 区域。似乎这种方法对于简单的形状(2D 切片中很少有不连贯的区域)很快,但对于复杂的形状(“树中的虫洞”)很慢。此外,需要一种快速算法来找到两组中的单个公共点。 (即不需要完整的集合交集,只需要集合是否具有至少一个公共点的信息。)

        同样,如果您的集合大小合理,Pham Trung 描述的简单算法可能是最佳选择。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-01
          • 1970-01-01
          • 2015-06-04
          相关资源
          最近更新 更多