Оптимизация перебора поверхностей, составленных из треугольниковИсточник: habrahabr
Встала задача перебрать все возможные варианты "триангуляций". Это "склейки" из N треугольников, которые подчиняются простым правилам:
Например, из трёх треугольников уникальных вариантов может быть всего два: При том, что всего вариантов склеек 120. Мне удалось неплохо оптимизировать процесс перебора, который позволил просчитать почти все варианты вплоть до N = 11, но это всё равно очень мало. Наивный подходРассмотрим вариант при N = 3. Три треугольника, 9 вершин. Нумеруем вершины 0...8 и Перебираем все от 000 000 000 до 888 888 888.
Комбинаторный подходДолгие вечера раздумий позволили голове родить следующую мысль: И действительно, самая длинная валидная склейка будет выглядеть так:
И видно, что в ней всего 5 уникальных вершин вместо 9. В общем случае количество уникальных вершин получается по формуле N + 2, где N - количество треугольников. Если уникальных вершин будет больше, то один из треугольников обязательно будет соединяться с другими лишь одной вершиной, а это неправильно. Итак. 5 уникальных вершин для трёх треугольников. Ну а теперь, считаем этот же биномиальный коэффициент для комбинаций из трёх треугольников В общем виде формула для количества комбинаций выглядит следующим образом:
Видите, сколько факториалов? Например, для N = 10 всего будет 59 473 554 359 599 446 комбинаций, это очень много, даже если параллелить. Когда я начал перебор с маленьких N (3,4,5..) то заметил, что программа довольно быстро находит все комбинации, а остальное - это просто дубликаты (они отсеиваются алгоритмом VF2, который проверяет графы на изоморфизм) Довольно быстро - это значит, что последняя "хорошая" склейка для, например, N = 5 нашлась на 2084 шаге из 324 632, что составляет 0.642% от всего диапазона. Для N = 6 это 0.3644%. Последнее, что мне удалось посчитать самому - для N = 8 это 0.1352%. То есть, в общем случае для N+1 мы смотрим, сколько процентов от предыдущего диапазона было посчитано и считаем столько же, это будет гарантировать нам, что всё проверено. Правда для N = 9 это все равно многовато (270 485 377 680, если проверять 0.1352%) Так что, надо было придумать что то еще.
Итеративный подходЕще несколько долгих вечеров и очередная идея: То есть, мы знаем, что для троек у нас их всего две [[A B C], [A B D], [A C D]] А четверок шесть [[A B C], [A B D], [A C D], [B C D]] Видно, что все четверки получились из предыдущих троек (перестановки генерятся в лексикографическом порядке) Всё бы хорошо, генери себе дальше, НО! Когда проверил этот подход, обнаружилось, что таким образом генерятся не все, например, пятёрки [[A B C], [A B D], [A C D], [B C E], [B D E]] Если внимательно приглядеться, то предпоследняя пятерка получилась отличающейся от всех четверок не на один, а на два треугольника. В терминах топологии это "лента мёбиуса". И если попытаться нарисовать её без одного, любого треугольника, то будет получаться ситуация, когда группы треугольников соприкасаются лишь одной вершиной, что совсем нехорошо. Что ж, выход все равно есть. Берем все получившиеся комбинации для N, отщипываем по одному треугольнику с конца, "догенериваем" еще все варианты двух и проверяем. Всё равно в кучу раз быстрее, чем с комбинаторным перебором. Так что, начиная с N = 9 отщипываются уже 2 треугольника, и "догенериваются" три. Это позволило просчитать почти все варианты вплоть до N = 11 и начало 12 ( почти - потому, что скорей всего там есть склейки, отличающиеся на 3, а то и больше треугольников). При том, что я распараллелил всё это дело. Один поток у меня выполняет первичную проверку склеек, остальные 3 занимаются отсечением дубликатов, т.к. проверка графов на изоморфность довольно ресурсоёмкое занятие. В общем, если не нужны все варианты, то можно отщипывать (или не отщипывать) и догенеривать оставшиеся, это будет конечно плохо, но какие то варианты это способ точно даст даже для больших N. Потому, что совершенно непонятно сколько и когда надо "отщипывать" чтобы получить весь набор валидных склеек. Но интересен то весь! Я уже и на тему перебора графов материал искал, и чего только не думал, круче итеративного метода пока нет идей. Может быть у вас будут мысли на эту тему? Например, когда генерируется комбинация треугольников, то первая всегда идет такая примерно склейка: ABC ABD ABE ABF ABG… P.S. это нужно для научной работы по классификации поверхностей. На меня конечно не обидятся, если я предоставлю данные до N = 11, у них и таких нет (хотя, например, тор можно построить из 14 треугольников, мне такое еще год обсчитывать). Но было бы интересно придумать способ который позволил бы это N отодвинуть подальше. Спасибо за внимание. UPD Стоило допереть раньше, но доперло только сейчас. Когда мы отщепляем пару треугольников от склейки из N, у нас получается куча явных дубликатов, которые можно убрать еще до начала перебора для N+1. Неплохой буст в скорости получается |