Эффективное заполнение пустых ячеек в 3D-матрице

#algorithm #data-structures #matrix

#алгоритм #структуры данных #матрица

Вопрос:

У меня есть 3D «кубическая» матрица, в которой некоторые ячейки заполнены, а другие пусты. Замкнутая область, окруженная заполненными ячейками, представляет собой полую форму. Например, в матрице ячейки могут быть заполнены таким образом, что вместе они образуют поверхность полой сферы. Теперь мне нужен эффективный способ заполнить внутреннюю часть этой сферы: если ячейка C0 окружена во всех направлениях заполненными ячейками (заполненная ячейка в любом направлении не обязательно должна быть ближайшим соседом C0 ), затем заполните C0 .

Наивный способ был бы следующим :-

Для каждой ячейки сканируйте в направлении 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], …, до конца сетки) должны быть частьювнешний и, следовательно, должен оставаться незаполненным.

Может кто-нибудь предложить более эффективный подход к этой проблеме? Приветствуются даже простые оптимизации, подобные приведенным выше, которые могут не уменьшить порядок временной сложности.

Комментарии:

1. Поиск крайних значений X, Y и Z может помочь?

2. Ваша полая форма имеет две открытые стороны? Нравится это ?

3. @PhamTrung Нет. Форма, которую я описал, представляет собой замкнутые пустотелые формы без какого-либо отверстия между ними.

4. @Nullpointer, я тебя не понял. Пожалуйста, уточните.

5. Итак, ваша проблема имеет 2 подзадачи: найдите пустоту, затем заполните ее?

Ответ №1:

Вы имеете дело с 3D заливкой. Смотрите подробную статью в Википедии http://en.m.wikipedia.org/wiki/Flood_fill

Ответ №2:

Хорошо, поскольку это закрытые пустотелые формы, мы можем просто использовать BFS или DFS для решения проблемы.

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 и посмотрите, не встретите ли вы заполненную ячейку в каждом направлении.

Если заполненная ячейка встречается в каждом направлении, затем заполните эту ячейку (поскольку она является частью внутренней части некоторой фигуры).

Приведенное выше утверждение неверно, если вы не имеете дело только с выпуклыми оболочками. На изображении ниже показано, что рассматриваемая точка не заключена в синюю фигуру, но она все равно будет пересекаться во всех направлениях (x, y, z).

введите описание изображения здесь

Вместо этого, чтобы справиться с общим случаем поиска полых фигур, вы можете добавить все ячейки в набор. Затем начните с граничной ячейки. Ячейка на границе является частью полой формы, если она заполнена, в противном случае она является частью фоновой (незаполненной) формы.

Затем, подобно ответу @Pham Trung, вы можете перемещаться наружу во всех направлениях, пока не пройдете все ячейки, которые находятся внутри фигуры, игнорируя цветные ячейки на границах. Выберите другую ячейку на границе предыдущей фигуры и начните процесс заново, пока не будут пройдены все ячейки.

В итоге каждая ячейка будет помечена либо как часть полой формы, либо как фон.

Ответ №4:

Просто для полноты, еще два. YMMV зависит от множества факторов.

1. Найдите поверхность

Если вы имеете дело с большим количеством вокселов, одной из возможностей оптимизации было бы найти граничную поверхность углубления. Это можно сделать, как в ответе Фама Трунга, но принимать только ячейки, в которых заполнен хотя бы один из их 6 соседей.

После определения граничной поверхности ее можно заполнить построчно, используя одномерные заливки, поскольку направления «внутри» и «снаружи» известны.

Этот метод позволяет значительно уменьшить размер набора, если у вас большое количество вокселов (масштабируется как n ^ 2 вместо n ^ 3). Поиск набора обычно выполняется очень быстро, но если набор не помещается в оперативную память, он сильно замедляется.

2. Срез до 2D

Другой возможностью было бы нарезать фигуру на 2D-фрагменты и соединить полученные полости слой за слоем. Тогда в памяти одновременно должны храниться только два фрагмента.

Основная идея состоит в том, чтобы присвоить каждой отдельной связанной 2D-области собственный идентификатор, а затем найти ее соединения с уже известными областями в соседнем слое. После обработки всех слоев остаются связанные 3D-области.

Сложной задачей является поиск наилучшего алгоритма для соединения 2D-областей в соседних слоях. Кажется, что этот метод работает быстро с простыми формами (несколько несвязанных областей в 2D-срезах), но медленно со сложными формами («червоточины в дереве»). Кроме того, необходим быстрый алгоритм для поиска одной общей точки в двух наборах. (Т.е. Полное пересечение множеств не требуется, только информация о том, имеют ли множества хотя бы одну общую точку или нет.)

Опять же, если ваши наборы имеют разумный размер, тривиальный алгоритм, описанный Pham Trung, вероятно, является лучшим выбором.