Введение
В рамках более крупной программы (связанной с рендерингом объемной графики) у меня есть небольшая, но сложная подзадача, где произвольная (но конечная) треугольная 2D-сетка должна быть помечена определенным образом. Уже некоторое время назад я написал решение (см. Ниже), которое было достаточно хорошим для тестовых сеток, которые у меня были в то время, хотя я понял, что подход, вероятно, не будет работать очень хорошо для каждой возможной сетки, о которой можно было бы подумать. Теперь я, наконец, столкнулся с сеткой, для которой настоящее решение вообще не работает, и похоже, что я должен придумать совершенно другой подход. К сожалению, мне кажется, что я не способен к reset моим линиям мышления, поэтому я думал, что попрошу здесь.
Проблема
Рассмотрим рисунок ниже. (Цвета не являются частью проблемы, я просто добавил их, чтобы улучшить (?) Визуализацию. Также переменная ширина края является абсолютно несущественным артефактом.)
Для каждого треугольника (например, оранжевого ABC и зеленого ABD) каждому из трех ребер необходимо присвоить одну из двух меток, например "0" или "1" . Есть только два требования:
- Не все края треугольника могут иметь одну и ту же метку. Другими словами, для каждого треугольника должны быть два "0" и один "1" , или два "1" и один "0" .
- Если ребро разделяется двумя треугольниками, он должен иметь одну и ту же метку для обоих. Другими словами, если ребро AB на изображении обозначено "0" для треугольника ABC, оно также должно быть помечено как "0" для ABD.
Меша является подлинной двумерной, и она конечна: т.е. она не обертывается и имеет четко определенную внешнюю границу. Очевидно, что на границе довольно легко удовлетворить требования, но внутри этого становится все труднее.
Интуитивно, похоже, по крайней мере одно решение всегда должно существовать, хотя я не могу это доказать. (Обычно их несколько - любой из них достаточно.)
Текущее решение
Мое текущее решение - действительно грубая сила (предоставленная здесь только для полноты), не пропустите этот раздел):
- Поддержание четырех наборов треугольников - по одному для каждого возможного количества (0..3) ребер, оставшихся помеченными. В начале каждый треугольник находится в наборе, где помечаются три ребра.
- Пока существуют треугольники с немаркированными краями:
Найдите наименьшее ненулевое число нераспределенных ребер, для которых остались еще треугольники. Другими словами: в любой момент времени мы пытаемся минимизировать количество треугольников, для которых эта маркировка была частично завершена. Количество оставшихся ребер будет составлять от 1 до 3. Затем просто выберите один такой треугольник с таким количеством оставшихся ребер, которое будет выделено. Для этого треугольника выполните следующие действия:- Убедитесь, что маркировка любого оставшегося края уже введена с помощью маркировки какого-либо другого треугольника. Если это так, назначьте метки, как это указано в требовании № 2 выше.
- Если это приводит к тупику (т.е. требование № 1 больше не может быть выполнено для настоящего треугольника), то сначала начните весь процесс.
- Выделите все оставшиеся края следующим образом:
- Если до сих пор не было отмечено никаких ребер, назначьте первый случайный случай.
- Когда уже выделено одно ребро, назначьте второй, чтобы он имел противоположную метку.
- Когда выделены два ребра: если они имеют один и тот же ярлык, назначьте третье, чтобы иметь противоположную метку (очевидно); если эти два имеют разные метки, назначьте третий случайным образом.
- Обновление наборов треугольников для разных значений нераспределенных ребер.
- Если мы когда-нибудь доберемся, у нас есть решение - ура!
Обычно этот подход находит решение всего за пару итераций, но в последнее время я столкнулся с сеткой, для которой алгоритм имеет тенденцию заканчиваться только после одной или двух тысяч попыток... что, очевидно, предполагает, что могут быть сетки, для которых он никогда не заканчивается.
Теперь я хотел бы иметь детерминированный алгоритм, который гарантированно всегда найдет решение. Вычислительная сложность не такая уж большая проблема, потому что сетки не очень большие, а маркировка в основном должна выполняться только при загрузке новой сетки, что не происходит все время - поэтому алгоритм с (например) экспоненциальным сложность должна быть прекрасной, пока она работает. (Но, конечно же: чем эффективнее, тем лучше.)
Спасибо, что прочитали это. Теперь любая помощь будет принята с благодарностью!
Изменить: результаты, основанные на предлагаемых решениях
К сожалению, я не могу получить подход, предложенный Dialecticus для работы. Может быть, я не понял это правильно. Во всяком случае, рассмотрим следующую сетку, с начальной точкой, обозначенной зеленой точкой:
Позвольте немного увеличить масштаб...
Теперь давайте запустим алгоритм. После первого шага маркировка выглядит следующим образом (red = "starred paths", blue = "ringed paths" ):
Все идет нормально. После второго шага:
И третье:
... четвертый:
Но теперь у нас есть проблема! Позвольте сделать еще один раунд, но обратите внимание на треугольник, нанесенный на пурпурный цвет:
Согласно моей текущей реализации, все края пурпурного треугольника находятся на кольцевом пути, поэтому они должны быть синими, что эффективно делает это контрпримером. Теперь, может быть, я как-то ошибся... Но в любом случае два края, которые ближе всего к началу node, очевидно, не могут быть красными; и если третий помечается красным, то кажется, что решение больше не подходит к идее.
Btw, здесь используются данные. Каждая строка представляет одно ребро, а столбцы должны интерпретироваться следующим образом:
- Индекс первого node
- Индекс второго node
- x координата первого node
- y координата первого node
- x координата второго node
- y координата второго node
Начало node - это индекс, имеющий индекс 1.
Я предполагаю, что в следующий раз я должен попробовать метод, предложенный Rafał Dowgird... Но, возможно, я должен сделать что-то совершенно другое на некоторое время:)